Upload
yasuhiko-yamamoto
View
528
Download
0
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
Windows ストア アプリでスレッド間の排他はどうする?
2014/5/10 @biac
非同期勉強会
2014/5/10 1
スピーカー紹介: 山本 康彦
• 宇宙世紀以前の生まれスプートニク1号より3ヶ月ほど前
• 最初は HONDAクルマの設計/研究を10年くらいやってた
• 今は BluewaterSoftを名乗ってアプリ開発とか技術解説記事とか
• 昨年7月に出した本⇒技術評論社「C#によるマルチコアのための非同期/並列処理プログラミング」
2014/5/10 2
今日の話は…
• 前半は、この本のPART4 からWindows ストア アプリで非同期/並列処理の例
• 後半は、この本には書かなかった話Windows ストア アプリの場合、スレッド間でロックするには?
2014/5/10 3
WINDOWS ストア アプリで非同期/並列処理の例
しばらく前振りだよ
2014/5/10 4
async/await, Parallel/PLINQ …
• このごろの .NET Framework非同期/並列処理も手厚くサポート
• 非同期: async/await キーワード非同期メソッドの呼び出しと、その後のコールバック処理をスムースにコーディング
• 並列: Parallelクラス/PLINQループ処理を自動的に並列化
2014/5/10 5
Windows デバイス アプリ
• いわゆる「Metro スタイル アプリ」Windows 8.x, Windows Phone 7~,近い将来 Xbox も解禁か?さらには IoT (Internet of Things) にも
• async/await を多用したコーディング用意されている API が非同期ばっかりだから!
2014/5/10 6
Metro アプリやってる人は、もうとっくに慣れたよね!? f(^^;
非同期/並列化の例
• この本の PART 4ソースコードはダウンロード可http://gihyo.jp/book/2013/978-4-7741-5828-0/
2014/5/10 7
サポート ページからダウンロード
できます
Langton's loops (ラングトンのループ)
• 詳しくは Wikipedia 参照 (できれば英語版を)
2014/5/10 8
セル オートマトンの一種
Langton's loops
2014/5/10 9
セルの状態は、・四方のセルの状態に依存・219パターン
これを全てのセルごと (画面上のドットごと) に計算する ⇒ けっこうな計算量
Langton's loops
• すなおに実装してみる1. 四方の値を読み取って配列に格納する (×セルの数だけ繰り返す)2. 次の状態を計算する (×N回)3. 画面を描き変える4. 1.に戻る
2014/5/10 10
表示を描き変える
四方を観測する×N回
次の状態を決定する×N回
Langton's loops
• 素直な実装で動かしてみる
2014/5/10 11
0.08秒/ サイクル
Langton's loops
• 非同期化+並列化+α・計算と画面描画を非同期化
Task, async/await
・観測と状態決定のループを並列化Parallel.For
・その他、ロジックのチューニング2つのループを1つに etc.
2014/5/10 12
表示を描き変える
四方を観測する×N回
次の状態を決定する×N回
並列実行
並列実行バッファ配列
Langton's loops
• 非同期+並列化+α の効果は!?
2014/5/10 13
0.009秒/ サイクル
1ケタ速くなった!!
Langton's loops
• 非同期化+並列化+α大事な話: スレッド間のロックを使っていない!
トラブルの元になるロックは、なるべく使わずにすむように設計する
2014/5/10 14
表示を描き変える
四方を観測する×N回
次の状態を決定する×N回
並列実行
並列実行バッファ配列
ロックは敵だ
• ロックするからデッドロックに嵌まるロックしなけりゃデッドロックは起きないマルチスレッドもデータベースも
• ロック不要の設計を心掛ける
• それでも必要なら当然使う.NET Framework には、単純なロック以外にも便利な仕掛けがあるよ♪
2014/5/10 15
WINDOWS ストア アプリでスレッド間の排他処理
さて、ここからが本題
2014/5/10 16
スレッド間競合の例【1】
• Windows ストア アプリでよくある例
• 例えば、複数のダウンロードと後処理を並列で行う⇒ 後処理で同じリソースを
更新すると、競合
2014/5/10 17
ダウンロード×N
データ処理×N
並列実行
バインディングソース
並列実行
スレッド間競合の例【1】
• 注: 普通は、時間の掛かるダウンロードだけ並列実行にして、後処理はシリアルにやればいい。※ @IT「Windowsストア・アプリ開発入門」第6回 のサンプルコードのコメントを参照
2014/5/10 18
スレッド間競合の例【2】
• バックグラウンド タスクの終了トリガー⇒ いつ呼び出されるか分からない
• 通常処理でも使うメソッドをトリガーから呼び出していると、競合する
2014/5/10 19
private async void FooAsync(){
……}
UI スレッドから呼び出しUI スレッド
private async void Button_Click(…){
await FooAsync();
バックグラウンド タスクのスレッドから呼び出し
終了トリガーを受けるメソッド (別スレッド)
private async void onTaskCompleted(…){
await FooAsync();
スレッド間競合の例【3】
• UI のイベント ハンドラー⇒ 呼び出し側は await してくれない
未完了でも次のイベントが走る
2014/5/10 20
private async void FooAsync(){
……}
UI スレッド
private async void Button_Click(…){
await FooAsync();}
❶❶
❷❶が await で抜けるFooAsync 未完了でも❷が走る
❷
【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"
データバインド
【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
データを取得して書き戻すまでの間、排他処理にしたい!
しょうがない、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 ステートメント本体では使用できません。」
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
await してると lock が使えない!さぁ、どうしよう?
スレッドに結び付いてるロックの仕掛けはどれも使えないぞ
2014/5/10 25
ロックに使える主な仕掛け
thread affinityスレッドに結び付く
• Monitor= lock(c#) / SyncLock(VB)
• SpinLockごく短時間のロックならMonitorより高パフォーマンス
• ReaderWriterLockSlim書き込みは1スレッド限定、読み出しは複数スレッドOK
• Mutexプロセス間でのロックも可 (Windowsデバイス アプリを除く)
スレッドと無関係
• SemaphoreSlimロックの獲得と解放を別スレッドで行ってもOK
2014/5/10 26
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
もっと便利に! 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 構文で、解放忘れと無縁に!
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
まとめ• 非同期/並列をうまく
使って、凍らない UI と高速化を実現しよう
• ロックは敵だ!必要最小限にとどめる設計が肝心
• await してると lock は使えない⇒ SemaphoreSlim を使う
2014/5/10 30
サンプル コードとこの資料
OneDrive に置いておきます。サンプル コードのビルドには VS 2013 Update 2 が必要 (Exp.可)http://1drv.ms/1kKv12g
2014/5/10 31
universal Windows apps
ご清聴ありがとうございました
2014/5/10 32
http://1drv.ms/1kKv12g