32
Windows ストア アプリで スレッド間の排他はどうする? 2014/5/10 @biac 非同期勉強会 2014/5/10 1

Windows ストア アプリでスレッド間排他処理

Embed Size (px)

DESCRIPTION

非同期勉強会@大阪 2014/5/10 PowerPoint 資料: http://1drv.ms/1jNrgh7 デモアプリ Windows 8.1: http://apps.microsoft.com/windows/ja-jp/app/windows/c18c112f-8863-45c1-8e2d-06e139156726 Windows Phone 8.1: http://www.windowsphone.com/s?appid=e0140fe2-7314-4b9a-baed-feb9562c189b

Citation preview

Page 1: Windows ストア アプリでスレッド間排他処理

Windows ストア アプリでスレッド間の排他はどうする?

2014/5/10 @biac

非同期勉強会

2014/5/10 1

Page 2: Windows ストア アプリでスレッド間排他処理

スピーカー紹介: 山本 康彦

• 宇宙世紀以前の生まれスプートニク1号より3ヶ月ほど前

• 最初は HONDAクルマの設計/研究を10年くらいやってた

• 今は BluewaterSoftを名乗ってアプリ開発とか技術解説記事とか

• 昨年7月に出した本⇒技術評論社「C#によるマルチコアのための非同期/並列処理プログラミング」

2014/5/10 2

Page 3: Windows ストア アプリでスレッド間排他処理

今日の話は…

• 前半は、この本のPART4 からWindows ストア アプリで非同期/並列処理の例

• 後半は、この本には書かなかった話Windows ストア アプリの場合、スレッド間でロックするには?

2014/5/10 3

Page 4: Windows ストア アプリでスレッド間排他処理

WINDOWS ストア アプリで非同期/並列処理の例

しばらく前振りだよ

2014/5/10 4

Page 5: Windows ストア アプリでスレッド間排他処理

async/await, Parallel/PLINQ …

• このごろの .NET Framework非同期/並列処理も手厚くサポート

• 非同期: async/await キーワード非同期メソッドの呼び出しと、その後のコールバック処理をスムースにコーディング

• 並列: Parallelクラス/PLINQループ処理を自動的に並列化

2014/5/10 5

Page 6: Windows ストア アプリでスレッド間排他処理

Windows デバイス アプリ

• いわゆる「Metro スタイル アプリ」Windows 8.x, Windows Phone 7~,近い将来 Xbox も解禁か?さらには IoT (Internet of Things) にも

• async/await を多用したコーディング用意されている API が非同期ばっかりだから!

2014/5/10 6

Metro アプリやってる人は、もうとっくに慣れたよね!? f(^^;

Page 7: Windows ストア アプリでスレッド間排他処理

非同期/並列化の例

• この本の PART 4ソースコードはダウンロード可http://gihyo.jp/book/2013/978-4-7741-5828-0/

2014/5/10 7

サポート ページからダウンロード

できます

Page 8: Windows ストア アプリでスレッド間排他処理

Langton's loops (ラングトンのループ)

• 詳しくは Wikipedia 参照 (できれば英語版を)

2014/5/10 8

セル オートマトンの一種

Page 9: Windows ストア アプリでスレッド間排他処理

Langton's loops

2014/5/10 9

セルの状態は、・四方のセルの状態に依存・219パターン

これを全てのセルごと (画面上のドットごと) に計算する ⇒ けっこうな計算量

Page 10: Windows ストア アプリでスレッド間排他処理

Langton's loops

• すなおに実装してみる1. 四方の値を読み取って配列に格納する (×セルの数だけ繰り返す)2. 次の状態を計算する (×N回)3. 画面を描き変える4. 1.に戻る

2014/5/10 10

表示を描き変える

四方を観測する×N回

次の状態を決定する×N回

Page 11: Windows ストア アプリでスレッド間排他処理

Langton's loops

• 素直な実装で動かしてみる

2014/5/10 11

0.08秒/ サイクル

Page 12: Windows ストア アプリでスレッド間排他処理

Langton's loops

• 非同期化+並列化+α・計算と画面描画を非同期化

Task, async/await

・観測と状態決定のループを並列化Parallel.For

・その他、ロジックのチューニング2つのループを1つに etc.

2014/5/10 12

表示を描き変える

四方を観測する×N回

次の状態を決定する×N回

並列実行

並列実行バッファ配列

Page 13: Windows ストア アプリでスレッド間排他処理

Langton's loops

• 非同期+並列化+α の効果は!?

2014/5/10 13

0.009秒/ サイクル

1ケタ速くなった!!

Page 14: Windows ストア アプリでスレッド間排他処理

Langton's loops

• 非同期化+並列化+α大事な話: スレッド間のロックを使っていない!

トラブルの元になるロックは、なるべく使わずにすむように設計する

2014/5/10 14

表示を描き変える

四方を観測する×N回

次の状態を決定する×N回

並列実行

並列実行バッファ配列

Page 15: Windows ストア アプリでスレッド間排他処理

ロックは敵だ

• ロックするからデッドロックに嵌まるロックしなけりゃデッドロックは起きないマルチスレッドもデータベースも

• ロック不要の設計を心掛ける

• それでも必要なら当然使う.NET Framework には、単純なロック以外にも便利な仕掛けがあるよ♪

2014/5/10 15

Page 16: Windows ストア アプリでスレッド間排他処理

WINDOWS ストア アプリでスレッド間の排他処理

さて、ここからが本題

2014/5/10 16

Page 17: Windows ストア アプリでスレッド間排他処理

スレッド間競合の例【1】

• Windows ストア アプリでよくある例

• 例えば、複数のダウンロードと後処理を並列で行う⇒ 後処理で同じリソースを

更新すると、競合

2014/5/10 17

ダウンロード×N

データ処理×N

並列実行

バインディングソース

並列実行

Page 18: Windows ストア アプリでスレッド間排他処理

スレッド間競合の例【1】

• 注: 普通は、時間の掛かるダウンロードだけ並列実行にして、後処理はシリアルにやればいい。※ @IT「Windowsストア・アプリ開発入門」第6回 のサンプルコードのコメントを参照

2014/5/10 18

Page 19: Windows ストア アプリでスレッド間排他処理

スレッド間競合の例【2】

• バックグラウンド タスクの終了トリガー⇒ いつ呼び出されるか分からない

• 通常処理でも使うメソッドをトリガーから呼び出していると、競合する

2014/5/10 19

private async void FooAsync(){

……}

UI スレッドから呼び出しUI スレッド

private async void Button_Click(…){

await FooAsync();

バックグラウンド タスクのスレッドから呼び出し

終了トリガーを受けるメソッド (別スレッド)

private async void onTaskCompleted(…){

await FooAsync();

Page 20: Windows ストア アプリでスレッド間排他処理

スレッド間競合の例【3】

• UI のイベント ハンドラー⇒ 呼び出し側は await してくれない

未完了でも次のイベントが走る

2014/5/10 20

private async void FooAsync(){

……}

UI スレッド

private async void Button_Click(…){

await FooAsync();}

❶❶

❷❶が await で抜けるFooAsync 未完了でも❷が走る

Page 21: Windows ストア アプリでスレッド間排他処理

【3】のサンプル コード

2014/5/10 21

UI スレッド private async void UpButton_Click(……){

await AddAsync(+1); // ⇒次ページ}

private async void DownButton_Click(……){

await AddAsync(-1); // ⇒次ページ}

private int GetData() // データを取得するユーティリティ メソッド{int num;int.TryParse(defaultViewModel["NumData"] as string, out num);return num;

}

private void SetData(int n) // データをセットするユーティリティ{defaultViewModel["NumData"] = n.ToString();

}

"NumData"

データバインド

Page 22: Windows ストア アプリでスレッド間排他処理

【3】のサンプル コード (続き)

2014/5/10 22

UI スレッド private Task AddAsync(int n){

return Task.Run( // 別スレッドで非同期に実行するasync () =>{

int num = GetData();

// await を含む長時間掛かる処理await Task.Delay(1000);

SetData(num + n);});

}

"NumData"

データバインド

これを実行してみる。例えば、素早く [UP] → [DOWN] すると…期待値: 0 ⇔ 実際: -1 …ダメぢゃん orz

データを取得して書き戻すまでの間、排他処理にしたい!

Page 23: Windows ストア アプリでスレッド間排他処理

しょうがない、lock するか…

private Task AddAsync(int n){return Task.Run(

async () =>{lock (this){

int num = GetData();

// await を含む長時間掛かる処理await Task.Delay(1000);

SetData(num + n);}

});}

2014/5/10 23

えぇっ!コンパイル エラー!?

なぜ…???

↑ 「エラー1 'await' 演算子は、lock ステートメント本体では使用できません。」

Page 24: Windows ストア アプリでスレッド間排他処理

lock の元ネタ Monitor で確認

private Task AddAsync(int n){return Task.Run(

async () =>{

Monitor.Enter(this);try{int num = GetData();

// await を含む長時間掛かる処理await Task.Delay(1000);

SetData(num + n);}finally{

Monitor.Exit(this);}

});}

2014/5/10 24

await の前後でスレッドが変わる!

Monitor.Enter と Exit は同じスレッドで呼び出さないといけない(thread affinity)

※ UI スレッドで await した場合は、UI スレッドで継続される

実行時エラー: System.Threading.SynchronizationLockException

Page 25: Windows ストア アプリでスレッド間排他処理

await してると lock が使えない!さぁ、どうしよう?

スレッドに結び付いてるロックの仕掛けはどれも使えないぞ

2014/5/10 25

Page 26: Windows ストア アプリでスレッド間排他処理

ロックに使える主な仕掛け

thread affinityスレッドに結び付く

• Monitor= lock(c#) / SyncLock(VB)

• SpinLockごく短時間のロックならMonitorより高パフォーマンス

• ReaderWriterLockSlim書き込みは1スレッド限定、読み出しは複数スレッドOK

• Mutexプロセス間でのロックも可 (Windowsデバイス アプリを除く)

スレッドと無関係

• SemaphoreSlimロックの獲得と解放を別スレッドで行ってもOK

2014/5/10 26

Page 27: Windows ストア アプリでスレッド間排他処理

SemaphoreSlim で解決♪

private SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);private Task AddAsync(int n){

return Task.Run(async () =>{

await _semaphore.WaitAsync();try {int num = GetData();

// await を含む長時間掛かる処理await Task.Delay(1000);

SetData(num + n);

}finally {

_semaphore.Release(); // 違うスレッドで解放してもOK}

});}

2014/5/10 27

Page 28: Windows ストア アプリでスレッド間排他処理

もっと便利に! AsyncLock クラス

private AsyncLock _asyncLock = new AsyncLock();private Task AddAsync(int n){return Task.Run(

async () =>{

using (await _asyncLock.LockAsync()){

int num = GetData();

// await を含む長時間掛かる処理await Task.Delay(1000);

SetData(num + n);}

});}

2014/5/10 28

AsyncLock クラスは、内部的に SemaphoreSlim を使っている。using 構文で、解放忘れと無縁に!

Page 29: Windows ストア アプリでスレッド間排他処理

AsyncLock クラスのソース

• Comparing two techniques in .NET Asynchronous Coordination Primitives- Scott Hanselmanhttp://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx

• Building Async Coordination Primitives, Part 6: AsyncLock- .NET Parallel Programming- MSDN Blogshttp://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx

2014/5/10 29

Page 30: Windows ストア アプリでスレッド間排他処理

まとめ• 非同期/並列をうまく

使って、凍らない UI と高速化を実現しよう

• ロックは敵だ!必要最小限にとどめる設計が肝心

• await してると lock は使えない⇒ SemaphoreSlim を使う

2014/5/10 30

Page 31: Windows ストア アプリでスレッド間排他処理

サンプル コードとこの資料

OneDrive に置いておきます。サンプル コードのビルドには VS 2013 Update 2 が必要 (Exp.可)http://1drv.ms/1kKv12g

2014/5/10 31

universal Windows apps

Page 32: Windows ストア アプリでスレッド間排他処理

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

2014/5/10 32

http://1drv.ms/1kKv12g