Thread affinity and CPS

32
Thread affinity And CPS 2016.05.20 CONTINUATION STUDY 2016 KOUJI MATSUI (@KEKYO2)

Transcript of Thread affinity and CPS

Page 1: Thread affinity and CPS

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

Page 2: Thread affinity and CPS

自己紹介

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

ロードバイク乗り

Microsoft MVP for Visual Studio and Development Technology

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

Center CLRオーガナイザー

C#, F#, C++

Page 3: Thread affinity and CPS
Page 4: Thread affinity and CPS

おことわり

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

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

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

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

Page 5: Thread affinity and CPS

アジェンダ

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

UIキューの隠蔽

非同期処理との融合

Page 6: Thread affinity and CPS

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

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

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Buttonclicked

UI thread context

Page 7: Thread affinity and CPS

処理をオフロードする

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

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Start

Offload workings

Race condition

Worker thread context

Page 8: Thread affinity and CPS

処理をオフロードする

リソース競合

Handler start

Replace textbox

Worker thread context

Modify UI statesRace condition

Race condition

Page 9: Thread affinity and CPS

スレッド親和性 (Thread affinity)

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

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

Page 10: Thread affinity and CPS

スレッド親和性を満たす

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

Start

Message pumps UI Queue

Handler Handler Handler

Start

Offload workings

Worker thread context

UI thread context

Page 11: Thread affinity and CPS

スレッド親和性を満たす

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

Start

Message pumps UI Queue

Update handler

HandlerClick

handler

Start

Offload workings

Page 12: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

UI Queue

Worker thread context

UI thread context

UI thread context

UI Queue

Page 13: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

UI Queue

UI Queue

Page 14: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

Page 15: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

Page 16: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

Page 17: Thread affinity and CPS

ハンドラの遷移

Click handler

Offload workings

Update handler

何となく見えてきた?

Page 18: Thread affinity and CPS

アジェンダ

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

UIキューの隠蔽

非同期処理との融合

Page 19: Thread affinity and CPS

UIキューを隠ぺいする

Click handler

Offload workings

Update handler

UI Queue

UI Queue

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

ワーカースレッド起動

Page 20: Thread affinity and CPS

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

ワーカースレッドの起動

継続を実行

Page 21: Thread affinity and CPS

.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

継続を実行

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

Page 22: Thread affinity and CPS

DispatcherSynchronizationContext

.NETにおいてのUIキュー

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

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

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

◦ WPFはDispatcherSynchronizationContext

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

UI Queue

Post()

SynchronizationContext

Page 23: Thread affinity and CPS

.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する

Page 24: Thread affinity and CPS

インフラとしての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)

直接継続できる

Page 25: Thread affinity and CPS

アジェンダ

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

UIキューの隠蔽

非同期処理との融合

Page 26: Thread affinity and CPS

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

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

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

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

Page 27: Thread affinity and CPS

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

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

構文糖による継続

async-awaitでもTaskを使う

Page 28: Thread affinity and CPS

.NET Task comparison

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

Page 29: Thread affinity and CPS

F# Async workflow

ほとんどasync-awaitと一緒

返されるのは Async<int>

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

Page 30: Thread affinity and CPS

つまり?

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

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

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

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

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

Page 31: Thread affinity and CPS

つまり?

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

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

Page 32: Thread affinity and CPS

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

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