Nimでロックと条件変数を提供する locks
はじめに
こんにちは。高校3年の樅山です。
今回は、Nimでロックと条件変数を提供する標準ライブラリ、locks
を解説します。
この記事は、Nim Advent Calendar 2020 その2 の15日目です。
他の標準ライブラリを調べる👇
locks
前回まで、Nimではマルチスレッドプログラミングをthreadsモジュールとchannelsモジュールでサポートしていることを説明してきました。
複数のスレッドから同時にデータへ書き込みを行うことで、データ競合に陥ります。
ロック
locksモジュールで提供されているロックを用いることで、明示的にアクセス制御をすることができます。
データ競合により適切に処理されない例
以下は、共有変数num
に複数のスレッドが更新を行うサンプルコードです。
全てが正しく加算されると、最終的にnum
の値は675
になりますが、データ競合によって625
になっています。
var
num = 0
workers: array[5, Thread[tuple[a, b: int]]]
proc task (interval: tuple[a, b: int]) {.thread.} =
for i in interval.a .. interval.b:
num += i
echo num - i, " => ", num
for i in 0..4:
createThread(workers[i], task, (i*10 , i*10+5))
workers.joinThreads()
echo num
-10 => 50
50 => 91
0 => 50
50 => 50
164 => 165
40 => 50
167 => 178
178 => 190
190 => 203
165 => 167
91 => 133
220 => 263
263 => 307
203 => 217
352 => 367
133 => 164
307 => 352
217 => 220
399 => 403
403 => 408
30 => 50
408 => 429
429 => 451
367 => 399
451 => 474
507 => 531
474 => 507
556 => 590
590 => 625
531 => 556
625
ロックを用いた実装
NimのロックはLock
型のオブジェクトで、initLock
プロシージャによって初期化できます。
guard
プラグマを用いて変数に対してロックオブジェクトを適用します。
ロックされた変数はデフォルトでアクセスが禁止されます。
これにアクセスするには、ロックを取得する必要があります。逆に、取得した後はロックを解放してアクセスできないように戻す必要があります。
withLock
プロシージャは、与えた変数のロックを取得してbodyに与えられた文の処理を行った後、ロックを解放します。
import locks
var
lock: Lock
num {.guard: lock.} = 0
workers: array[5, Thread[tuple[a, b: int]]]
proc task (interval: tuple[a, b: int]) {.thread.} =
for i in interval.a .. interval.b:
withLock lock:
num += i
echo num - i, " => ", num
lock.initLock
for i in 0..4:
createThread(workers[i], task, (i*10 , i*10+5))
workers.joinThreads()
echo num
0 => 10
10 => 21
21 => 33
33 => 73
73 => 114
114 => 156
156 => 176
176 => 197
197 => 219
219 => 242
242 => 266
266 => 291
291 => 321
321 => 352
352 => 395
395 => 439
439 => 484
484 => 484
484 => 485
485 => 487
487 => 490
490 => 494
494 => 499
499 => 512
512 => 526
526 => 541
541 => 573
573 => 606
606 => 640
640 => 675
675
このように、データ競合が発生せずに正しく処理されていることがわかります。
ロックを取得する
withLock
テンプレートを使用せず、ロックを取得するにはacquire
プロシージャを利用します。
proc acquire(lock: var Lock) {.raises: [], tags: [].}
また、成功するかわからない場合にはtryAcquire
プロシージャを呼び出します。成功した場合はtrue
を、失敗した場合はfalse
を返します。
proc tryAcquire(lock: var Lock): bool {.raises: [], tags: [].}
ロックを解放する
withLock
テンプレートを使用せず、ロックを解放するにはrelease
プロシージャを利用します。
proc release(lock: var Lock) {.raises: [], tags: [].}
条件変数
条件変数は、initCond
で初期化することができます。
proc initCond(cond: var Cond) {.inline, raises: [], tags: [].}
また、wait
に条件変数とロックを与えることで、条件を満たすまで処理を待機できます。
proc wait(cond: var Cond; lock: var Lock) {.inline, raises: [], tags: [].}
signal
に条件変数を与えると、スレッドにシグナルを送信します。
proc signal(cond: var Cond) {.inline, raises: [], tags: [].}
参考文献
Author And Source
この問題について(Nimでロックと条件変数を提供する locks), 我々は、より多くの情報をここで見つけました https://qiita.com/momeemt/items/f4e98e67217cdd9d2a6b著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .