LoginSignup
0
1

簡素な非同期排他処理の比較(クリティカルセクションの簡易自作/SyncLockの代用)

Last updated at Posted at 2023-07-20

きちんとまとめようとしてたらいつまでも投稿できないことに気が付いた。Done is better than perfectということで、15分ググって出てこない内容は自分でまとめて公有財産にしておこう...

前提条件

OS:windows10
IDE:VS2019, VB.Net framework v4.7.2
目的:ローカルで動作するアプリケーション中での非同期制御
(裏で通信しつつ、UI操作しつつ、処理実行)

概要

ググった結果

vb.netの排他制御のための方法はいくつかある。
目立つところの情報をまとめ、比較実験(C#だが)をやっていただいている良記事をみて満足していた。
https://qiita.com/tadokoro/items/28b3623a5ec58517d431

一番早いのはInterlocked(.Increment)、時点でSyncLockということになる。
そこまでパフォーマンスが致命的ではないし、1変数いじって終わりの超簡単な内容をやるわけではない(例えば特定データをtcpClientで送信する際にデータをブロックしておくなどをしたい)し、単純なクリティカルセクションに入るSyncLockで十分だろう...と思っていた。(Interlockedはいわゆるアトミック操作だが、初見だとExchangeあたりが面食らう)

SyncLockの問題点

使ってみればじきにわかる(というか当たり前)なのだが、

SyncLock LockObj
    'ここで排他処理
End SyncLock

このパターン、Do whileやTry Catchをまたぐことはできないのだ。
当然Awaitも使えない。
これは困る。
例外による分岐処理や、応答待ちができないのだ。

InterLockedを使う

残念ながら出典を見つけられないが、どこかのサイトに「だからInterlockedが作られた」とあった。
Interlocked.CompareExchangeを使えば、値の確認をスレッドセーフに行える。例えば

Do Until timeout
    If lockObj = False Then
        lockObj = True
        Exit Do
    End If
Loop

'ここで排他処理

lockObj = False

みたいなことをしたいとして(このままでは同時アクセスが起きた時に事故る)、

import system.Threading

Do Until timeout
    If False = Interlocked.CompareExchange(lockObj, True, False) Then
        Exit Do
    End If
Loop

'ここで排他処理

Interlocked.Exchange(lockObj, False) 

とすればスレッドセーフにできる(はず)なのだ。
そしてこれはブロックフリー(今考えた造語)=Try CatchやAwaitを自由に使うことができる。

もちろん、例外時に解放しないとデッドロックする。これはユーザーの責任

さて、パフォーマンスはどうだろう。

*ここに実験結果を貼る*

実験環境と投稿環境が違うのと、ちゃんと条件整理して実験できていないのでデータを貼れない。
やってみた感じ、さすがにSyncLockよりも遅い。大体3倍くらいの時間がかかる(気がする)
しかも、複数スレッドで回そうとするとInterlockedで書いたやつが(うまく動作せずに?)一瞬で終了するときがある。3倍かかるはずなのに。この辺、精査しないと使えないな... (たぶん実験のために適当に書いたコードがいけない。厳密に同時に複数スレッド・タスクたてて処理並列でやらせて処理時間比較するには...?)
まあ当然だろうな。
元記事ではInterlocked.Increment vs SyncLockをやって4倍差の結果。
こちらではInterlocked.CompareExchange+Exchange vs SyncLockをやって2.5-3倍差の結果。

CompareExchange+Exchange (+if判定、DoLoop抜け) = 12*Increment

ということだ。

追記
その後もう少し弄っていて、そもそもこのInterlockedの使い方が意図したとおりに動作していない
ことが判明した。具体的にはスレッドセーフにならなかった。えっ... ちょっとコード読んだだけではなぜなのかわかりません。
うーん、コンパイラが勝手に短絡評価してるんだろうか... volatileが必要と思われる(震え声)
あまりms learnにあまり説明がないmemorybarrierなるものも試してみたが、何の効果もないおまじないだった。あれなんなんですか。実装を知ってる人しかわかりそうにない。

結局のところsemaphore(Slim)を使うしかないというのが結論。記法的にはこれで十分ですな。

記事は以上。

0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1