Nimでスレッド間通信を提供する channels
はじめに
こんにちは。高校3年の樅山です。
今回は、Nimでスレッド間通信を提供する標準ライブラリ、channels
の解説をしたいと思います。
マルチスレッドの基本的な機能については、標準ライブラリ #12 threads を参照してください。
この記事は、Nim Advent Calendar 2020 その2 の14日目です。
Nim 標準ライブラリシリーズ
- Nimの文字列フォーマット strformat
- Nimで "集合" を扱う sets
- Nimで統計分析が行える stats
- Nimでジェネリックなアルゴリズムを使いこなそう algorithm
- Nimの配列を華麗に処理する sequtils
- Nimの型情報を提供する typetraits
- Nimで複素数を扱う complex
- Nimによるビット操作入門 bitops
- NimでCPUのコア数を調べよう cpuinfo
- Nimでバイトオーダーを処理する endians
- Nimで整数と浮動小数点数間の演算を提供する lenientops
- Nimでマルチスレッドプログラミングをはじめよう threads
- Nimでスレッド間通信を提供する channels
channels
channels
モジュールは、スレッド間通信を提供しています。
threads
と一緒に使うことを想定しているため、spawn
と組み合わせることは推奨されていません。不安定な動作を起こす可能性があります。
threads
モジュールと同様に --threads:on
スイッチを付与しなければコンパイルに失敗します。また、デフォルトで読み込まれているので明示的にimport
文を記述してはいけません。
簡単なスレッド通信
本記事では、Channel
によって生成されるスレッド通信を制御するオブジェクトをチャンネルと呼びます。
channels
モジュールが提供しているsend
プロシージャによって、スレッドがチャンネルに対してメッセージを送信することができます。
また、同モジュールが提供しているrecv
プロシージャによって、チャンネルがメッセージを受信することができます。
ここで、スレッドが送信したメッセージが受信可能になるまでの間、メインスレッドを占有して他の処理ができなくなる通信方法をブロッキング通信と呼びます。
recv
による受信は、ブロッキング通信となります。
import os
var channel: Channel[string]
proc task1 () =
channel.send("Simple Task!")
proc task2 () =
sleep(5000)
channel.send("Delay Task!")
channel.open()
var worker: Thread[void]
createThread(worker, task1)
echo channel.recv()
createThread(worker, task2)
echo channel.recv()
worker.joinThread()
channel.close()
Simple Task!
Delay Task!
送信したメッセージは、ディープコピーされる点に注意しましょう。
保持できる未処理のメッセージ数を設定する
open
プロシージャは、スレッド通信用のチャンネルを開く働きがあります。定義を見てみましょう。
proc open*[TMsg](c: var Channel[TMsg]; maxItems: int = 0)
デフォルト値で0
をとるパラメータ、maxItems
があります。
maxItems
が0
の時、保持できる未処理のメッセージは無制限であり、無制限キューとして働きます。
1
以上の値を与えると、未処理のメッセージ数に上限を設けることができます。
ここで、チャンネルがいくつメッセージを保持しているか調べるためにpeek
プロシージャを使いましょう。
注: peek
プロシージャを用いることでスレッドが競合状態に陥りやすくなるため、危険です。ソフトウェアの実装に利用するのはなるべく避けましょう。
import os
var channel: Channel[string]
proc task1 () =
channel.send("Hello, Nim!")
channel.open()
var worker: Thread[void]
for i in 1..5:
createThread(worker, task1)
for i in 1..5:
echo channel.peek
echo channel.recv()
worker.joinThread()
0
Hello, Nim!
4
Hello, Nim!
3
Hello, Nim!
2
Hello, Nim!
1
Hello, Nim!
無制限キューの場合は、全ての未処理メッセージがチャンネルに保持されました。
import os
var channel: Channel[string]
proc task1 () =
channel.send("Hello, Nim!")
channel.open(2)
var worker: Thread[void]
for i in 1..5:
createThread(worker, task1)
for i in 1..5:
echo channel.peek
echo channel.recv()
worker.joinThread()
0
Hello, Nim!
1
Hello, Nim!
1
Hello, Nim!
2
Hello, Nim!
1
Hello, Nim!
メッセージの上限数が2
の場合、保持されるメッセージ数も最大2
に制限されました。
また、peek
の代わりにready
プロシージャを用いることで、未送信のメッセージがあるかどうかを判定できます。
ノンブロッキング通信
ブロッキング通信に対して、メインスレッドをブロックせずに通信を送受信するのがノンブロッキング通信です。
recv
の代わりにtryRecv
で受信することでメインスレッドをブロックしなくなります。
import os
var channel: Channel[string]
channel.open()
proc task1() =
sleep(2000)
channel.send("国家機密")
var worker: Thread[void]
createThread(worker, task1)
while true:
let triedReceive = channel.tryRecv()
if triedReceive.dataAvailable:
echo triedReceive.msg
break
echo "重要なセキュリティ処理"
sleep(400)
worker.joinThread()
channel.close()
重要なセキュリティ処理
重要なセキュリティ処理
重要なセキュリティ処理
重要なセキュリティ処理
重要なセキュリティ処理
国家機密
ただし、メインスレッドをブロックしない代償にメッセージが必ず受信できるとは限らない点に注意しましょう。
recv
とtryRecv
の定義を比較してみましょう。
proc recv*[TMsg](c: var Channel[TMsg]): TMsg
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool, msg: TMsg]
ノンブロッキング通信では、受信が成功したかを表すdataAvailable
と、メッセージであるmsg
がタプルで返却されます。
上のコードのように、受信するまでwhile
ループで待ち続けて、dataAvailable
がtrue
になった時点でループを抜けると良いでしょう。
ノンブロッキングな送信
受信と同様に、送信でもtrySend
プロシージャを用いてノンブロッキングな送信を行うことができます。
import os
var channel: Channel[string]
proc task1 () =
echo channel.trySend("Hello, Nim!")
channel.open(2)
var worker: Thread[void]
for i in 1..5:
createThread(worker, task1)
while true:
let triedChannel = channel.tryRecv()
if not triedChannel.dataAvailable:
break
worker.joinThread()
true
false
true
false
false
trySend
プロシージャは、maxItems
の制限を超えるなど痩身に失敗した場合にfalse
を返します。
送信時に、メインスレッドはブロックされません。
終わりに
channels
モジュールを利用することで並列操作だけでなく、スレッド間の通信も利用できるようになります。
threads
もchannels
も非常に低レベルなAPIなので、実際には他の高レベルなスレッドライブラリを使う場合が多いですが、Nimではスレッドがどのように扱われているかを知っておくのは良いと思います。
ぜひマルチスレッドプログラミングに取り組んでみてください!
余談
学生はGitHub Proに無料で加入できるのですが、GitHub Proユーザーは連携することでCanvaのProプランが利用できるそうです。
本記事ではこれを利用して画像を作りましたが、有料素材を気にせず使えるのは非常に良いので、学生の皆様はぜひ手続きをしてみてください。
参考文献
Author And Source
この問題について(Nimでスレッド間通信を提供する channels), 我々は、より多くの情報をここで見つけました https://qiita.com/momeemt/items/531825c03ec6cc2d78a4著者帰属:元の著者の情報は、元の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 .