Thread affinity and CPS

Post on 21-Jan-2018

642 views 1 download

Transcript of Thread affinity and CPS

Thread affinityAndCPS2016.05.20 CONTINUATION STUDY 2016 KOUJI MATSUI (@KEKYO2)

自己紹介

けきょ (@kekyo2, www.kekyo.net)

ロードバイク乗り

Microsoft MVP for Visual Studio and Development Technology

認定スクラムマスター・スクラムプロダクトオーナー

Center CLRオーガナイザー

C#, F#, C++

おことわり

名古屋から来たアホです。なごやこわい界隈に生息してますが、メンツには含まれていません(たぶん)

継続の学問的な知識はほとんど無いです。◦そのため、学問的厳密性は無いです。

◦ぶっちゃけ、CPS的に画期的な話ではないと。

◦スイートスポットにはまる人には、面白いかも知れません(多分いない)

アジェンダ

ユーザーインターフェイスとスレッド親和性

UIキューの隠蔽

非同期処理との融合

ユーザーインターフェイス

ユーザーインターフェイススレッド

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Buttonclicked

UI thread context

処理をオフロードする

ユーザーインターフェイススレッド vs ワーカースレッド

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Start

Offload workings

Race condition

Worker thread context

処理をオフロードする

リソース競合

Handler start

Replace textbox

Worker thread context

Modify UI statesRace condition

Race condition

スレッド親和性 (Thread affinity)

全てのUI部品やUIハンドラは、特定のスレッドで動作することを前提としています。

特定のスレッドに紐づいているUI部品の、スレッド依存の状態を「スレッド親和性」と呼びます。

スレッド親和性を満たす

UIキューを介して通信を行う

Start

Message pumps UI Queue

Handler Handler Handler

Start

Offload workings

Worker thread context

UI thread context

スレッド親和性を満たす

ハンドラとオフロード処理の関係

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Start

Offload workings

ハンドラの遷移

Click handler

Offload workings

Update handler

UI Queue

Worker thread context

UI thread context

UI thread context

UI Queue

ハンドラの遷移

Click handler

Offload workings

Update handler

UI Queue

UI Queue

ハンドラの遷移

Click handler

Offload workings

Update handler

ハンドラの遷移

Click handler

Offload workings

Update handler

ハンドラの遷移

Click handler

Offload workings

Update handler

ハンドラの遷移

Click handler

Offload workings

Update handler

何となく見えてきた?

アジェンダ

ユーザーインターフェイスとスレッド親和性

UIキューの隠蔽

非同期処理との融合

UIキューを隠ぺいする

Click handler

Offload workings

Update handler

UI Queue

UI Queue

ワーカースレッドからUIスレッドに跨ぐときにUIキューが絡む

ワーカースレッド起動

UIキューを隠ぺいする

UIキューに要求(ワーカースレッド起動)を入れ、結果をUIキューから得るような f を考える。

let uiq = new Queue<(unit -> ‘U)>()let f (action: Func<‘T, ‘U>) (argument: ‘T) (cps: ‘U -> unit) =

Thread.Execute(fun _ ->let result = action(argument)uiq.Enqueue result)

while true dolet result = uiq.Dequeue()cps result

ワーカースレッドの起動

継続を実行

.NETにおいてのワーカースレッド

.NET標準のワーカースレッド起動は Task.Run() メソッド。

Task.Run() は Task<‘U> を返す。

TaskにはCPSを仕込む Task.ContinueWith()メソッドがある。

let uiq = new Queue<(unit -> ‘U)>()let f (action: Func<‘T, ‘U>) (argument: ‘T) (cps: ‘U -> unit) =

let task = Task.Run<‘U>(fun _ -> action(argument))task.ContinueWith(fun t -> uiq.Enqueue t.Result)while true do

let result = uiq.Dequeue()cps result

継続を実行

ワーカースレッド完了時にキューに入れる

DispatcherSynchronizationContext

.NETにおいてのUIキュー

.NET標準の同期化キューは SynchronizationContextクラス (SynchContext)。

SynchContext.Post() は、スレッド親和性が必要な場合に呼び出される。CPSを渡せば必要なスレッドで継続を実行することを「想定」できる。

SynchContextの実装は、クラスを継承して作る必要がある。◦ Windows FormsはWinFormsSynchronizationContext

◦ WPFはDispatcherSynchronizationContext

◦ を使えばいい(自動的に使われる)

UI Queue

Post()

SynchronizationContext

.NETにおいてのUIキュー

let f (action: Func<‘T, ‘U>) (argument: ‘T) (cps: ‘U -> unit) =let task = Task.Run<‘U>(fun _ -> action(argument))task.ContinueWith(fun t -> SynchContext.Current.Post(

(fun _ -> cps t.Result), null))ワーカースレッド完了時に

SynchContextにPostする

インフラとしてのSynchContext

実はSynchContextは.NETの奥底で統合されています。◦現在のスレッドに対応するSynchContextは、SynchContext.Currentでアクセスできます。

Task.ContinueWithによるCPSも、Currentのインスタンスを自動的に使います。

なので、前述のような明示的な Post は必要ありません。

let f (action: Func<‘T, ‘U>) (argument: ‘T) (cps: ‘U -> unit) =let task = Task.Run<‘U>(fun _ -> action(argument))task.ContinueWith(fun t -> cps t.Result)

直接継続できる

アジェンダ

ユーザーインターフェイスとスレッド親和性

UIキューの隠蔽

非同期処理との融合

.NET Task (.NET 4.0 / C# 4.0)

まずは、.NET 4.0 TaskのCPS (ContinueWithを使う)

関数に直接継続を渡していないが、Task.ContinueWithに継続を渡している≒ CPS

注: 手を抜いてasync使ってます

.NET Task (.NET 4.5/C# 5.0)

そして、.NET 4.5/C# 5.0 でのasync-awaitによる非同期処理

構文糖による継続

async-awaitでもTaskを使う

.NET Task comparison

これが .NET 4.0 (C# 4.0 Task only) こうなる .NET 4.5 (C# 5.0 async-await)

F# Async workflow

ほとんどasync-awaitと一緒

返されるのは Async<int>

F# Asyncクラスも内部でSynchContextを使ってスレッド親和性を担保します。

つまり?

UIの応答処理をワーカースレッドにオフロードしたい。

Task.ContinueWithを使い、CPSな継続処理を登録する。

登録されたCPSは、SynchContextを使ってスレッド親和性を担保する(正しいスレッドでCPSを実行する)。

上記インフラはTaskクラスで実現しているので、C#のasync-awaitでも機能する

F# Async workflowでもAsyncクラスがSynchContextを使うので、全く同じように機能する。

つまり?

TaskやAsyncは、ワーカースレッドだけではなく、I/O非同期処理を表すこともできる(むしろそっちが主体)。

これらを全て統一して扱い、なおかつスレッド親和性を担保するために、.NETの背景でCPSが継続しまくってますよ!!

ご清聴ありがとうございました!

スライドはブログに上げます◦ http://www.kekyo.net/