Thread affinity and CPS
-
Upload
kouji-matsui -
Category
Software
-
view
642 -
download
1
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が継続しまくってますよ!!