$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Rustでマルチスレッドプログラミング! リアルタイム通信ではどのようにスレット...
Search
yuki_uchida
November 29, 2024
6
2.2k
Rustでマルチスレッドプログラミング! リアルタイム通信ではどのようにスレッドを立てるのか
Rust.Tokyo 2024
https://rust.tokyo/lineup/4
で登壇した際に利用した発表資料です。
yuki_uchida
November 29, 2024
Tweet
Share
More Decks by yuki_uchida
See All by yuki_uchida
MoQとか勉強会#2 発表資料
yuki_uchida
2
630
SkyWayが遭遇したWebRTC の可観測性に関する問題と開発者向け可視化サービス提供までの道のり
yuki_uchida
4
3k
技術発信を続けるためのTIPS
yuki_uchida
3
120
何かの技術の"専門家"になりたかったから技術調査チームを立ち上げてプロダクトに貢献した話
yuki_uchida
6
500
メソッドチェーンを使ってDataFrameの可読性と保守性を向上させよう
yuki_uchida
1
320
仕様策定中のプロトコルを Rust で書いてブラウザで動かしてみた
yuki_uchida
3
2.4k
Media Over QuicTransportって知ってる?
yuki_uchida
0
440
WebRTCの歴史とこれから
yuki_uchida
0
1.2k
WebTransportのまとめと今後
yuki_uchida
5
1.4k
Featured
See All Featured
The Invisible Side of Design
smashingmag
298
50k
Fashionably flexible responsive web design (full day workshop)
malarkey
405
65k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Practical Orchestrator
shlominoach
186
10k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
160
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.4k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5k
Code Reviewing Like a Champion
maltzj
520
39k
KATA
mclloyd
29
14k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
Optimizing for Happiness
mojombo
376
70k
Transcript
Rustでマルチスレッドプログラミング! 〜リアルタイム通信ではどのようにスレッドを立てるのか〜 Rust.Tokyo 2024 NTTコミュニケーションズ 株式会社 内田 裕貴
内田 裕貴(Ucchy) X: @yuki_wtz NTTコミュニケーションズに2019年に新卒入社 WebRTC Platform SkyWayでResearchエンジニア Rustでメディアをリアルタイムにやり取りするプロトコルを実装(moq-wasm) IETFの相互接続試験に持ち込んで標準化貢献
セッションで話すこと(20分) マルチスレッドの必要性 ~Media over QUIC Transportを例に~ Rustでスレッドを立ててみる ~マルチスレッド化とメッセージのやり取り~ マルチスレッドの難しさとまとめ ~本セッションのコードの振り返り~
マルチスレッドの必要性 ~Media over QUIC Transportを例に~
Media over QUIC Transport(MoQT)とは
Media over QUIC Transport(MoQT)とは QUICをベースとしたリアルタイム通信のためのプロトコル 主に映像や音声などのサイズの大きいメディアを送受信できるように仕様が策定 されている
Media over QUIC Transport(MoQT)とは https://datatracker.ietf.org/meeting/interim-2023-moq-08/session/moq QUICをベースとしたリアルタイム通信のためのプロトコル 主に映像や音声などのサイズの大きいメディアを送受信できるように仕様が策定 されている
Media over QUIC Transport(MoQT)とは https://datatracker.ietf.org/meeting/interim-2023-moq-08/session/moq QUICをベースとしたリアルタイム通信のためのプロトコル 主に映像や音声などのサイズの大きいメディアを送受信できるように仕様が策定 されている 具体的なユースケースはビデオ会議・ライブ配信
Media over QUIC Transport(MoQT)とは 今のビデオ会議やライブ配信ではダメなのか?
Media over QUIC Transport(MoQT)とは 今のビデオ会議やライブ配信ではダメなのか? 現在では、ユースケースによってプロトコルを使い分けている ビデオ会議 WebRTC: 超低遅延志向なプロトコル ライブ配信
HLS: 高品質志向なプロトコル
Media over QUIC Transport(MoQT)とは 今のビデオ会議やライブ配信ではダメなのか? 現在では、ユースケースによってプロトコルを使い分けている ビデオ会議 WebRTC: 超低遅延志向なプロトコル ライブ配信
HLS: 高品質志向なプロトコル この二つのプロトコルでは遅延と品質のトレードオフを選択できない
Media over QUIC Transport(MoQT)とは 今のビデオ会議やライブ配信ではダメなのか? 現在では、ユースケースによってプロトコルを使い分けている ビデオ会議 WebRTC: 超低遅延志向なプロトコル ライブ配信
HLS: 高品質志向なプロトコル この二つのプロトコルでは遅延と品質のトレードオフを選択できない Media over QUIC Transportはこれらのプロトコルの両取りを目指す
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性 MoQTは、そのユースケース(ビデオ会議・ライブ配信)からわかるように、 リアルタイムに映像や音声が大量に流れる
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性 MoQTは、そのユースケース(ビデオ会議・ライブ配信)からわかるように、 リアルタイムに映像や音声が大量に流れる 映像は200kbpsから10Mbps 音声は32kbpsから256kbps
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性 MoQTは、そのユースケース(ビデオ会議・ライブ配信)からわかるように、 リアルタイムに映像や音声が大量に流れる 映像は200kbpsから10Mbps 音声は32kbpsから256kbps ビデオ会議では人数分のメディアが流れて約250kbps*人数分にもなる
ライブ配信では4K映像流したりすると10Mbpsにもなる
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性 MoQTは、そのユースケース(ビデオ会議・ライブ配信)からわかるように、 リアルタイムに映像や音声が大量に流れる 映像は200kbpsから10Mbps 音声は32kbpsから256kbps ビデオ会議では人数分のメディアが流れて約250kbps*人数分にもなる
ライブ配信では4K映像流したりすると10Mbpsにもなる メディアを中継するサーバーには非常に大きな負荷がかかる サーバーがどれだけメディアを捌けるかはサービスのコストに直結する
Media over QUIC Transport(MoQT)の難しさと マルチスレッドの必要性 MoQTは、そのユースケース(ビデオ会議・ライブ配信)からわかるように、 リアルタイムに映像や音声が大量に流れる 映像は200kbpsから10Mbps 音声は32kbpsから256kbps ビデオ会議では人数分のメディアが流れて約250kbps*人数分にもなる
ライブ配信では4K映像流したりすると10Mbpsにもなる メディアを中継するサーバーには非常に大きな負荷がかかる サーバーがどれだけメディアを捌けるかはサービスのコストに直結する マルチスレッド処理を活用してCPUをフルに使う必要がある
Rustでスレッドを立ててみる ~マルチスレッド化とメッセージのやり取り~
Rustにおけるスレッド
Rustにおけるスレッド スレッドには2種類ある
Rustにおけるスレッド スレッドには2種類ある OSスレッド(std::threadなど) グリーンスレッド (tokio::spawnなど)
OSスレッド グリーンスレッド Rustにおけるスレッド スレッドには2種類ある OSスレッド(std::threadなど) グリーンスレッド (tokio::spawnなど)
OSスレッド グリーンスレッド CPUコアとスレッドが1:1対応 OSスレッドの上で複数のグリーンスレッドが動作 Rustにおけるスレッド スレッドには2種類ある OSスレッド(std::threadなど) グリーンスレッド (tokio::spawnなど)
OSスレッド グリーンスレッド CPUコアとスレッドが1:1対応 OSスレッドの上で複数のグリーンスレッドが動作 OSがスケジューリング ランタイムがスケジューリング Rustにおけるスレッド スレッドには2種類ある OSスレッド(std::threadなど) グリーンスレッド
(tokio::spawnなど)
OSスレッド グリーンスレッド CPUコアとスレッドが1:1対応 OSスレッドの上で複数のグリーンスレッドが動作 OSがスケジューリング ランタイムがスケジューリング 作成・破棄・コンテキストスイッチが重い 作成・破棄・コンテキストスイッチが軽い Rustにおけるスレッド スレッドには2種類ある
OSスレッド(std::threadなど) グリーンスレッド (tokio::spawnなど)
Rustにおけるスレッド これらの使い分けは、CPU処理が重いかI/O処理が重いか
Rustにおけるスレッド これらの使い分けは、CPU処理が重いかI/O処理が重いか 映像のエンコード処理やAI処理をやり続けるような場合 => OSスレッド I/O処理など待ちが多い場合 => グリーンスレッド
Rustにおけるスレッド これらの使い分けは、CPU処理が重いかI/O処理が重いか 映像のエンコード処理やAI処理をやり続けるような場合 => OSスレッド I/O処理など待ちが多い場合 => グリーンスレッド Media over
QUIC TransportではI/O待ちが多いためグリーンスレッドを多用
スレッドを立ててみる
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別の メッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセー ジを転送する
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討 クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別の メッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセー ジを転送する
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討 クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別の メッセージが送信される 双方向ストリーム: C-Plane用メッセージ メディア情報や配信処理などの制御系メッセージ データは少ない
単方向ストリーム: D-Plane用メッセージ 映像や音声のデータメッセージ メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセー ジを転送する
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討 クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別の メッセージが送信される 双方向ストリーム: C-Plane用メッセージ メディア情報や配信処理などの制御系メッセージ データは少ない
単方向ストリーム: D-Plane用メッセージ 映像や音声のデータメッセージ データメッセージのせいで制御メッセージの処理が詰まるのは避けたい メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセー ジを転送する
スレッドを立ててみる メディアの中継サーバーの処理から、スレッドを分けて処理するのが良い部分を検討 クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別の メッセージが送信される 双方向ストリーム: C-Plane用メッセージ メディア情報や配信処理などの制御系メッセージ データは少ない
単方向ストリーム: D-Plane用メッセージ 映像や音声のデータメッセージ データメッセージのせいで制御メッセージの処理が詰まるのは避けたい メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセー ジを転送する スレッド間でのメッセージのやり取りを行いたい
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する connectionごとの処理が完了しなければ次のconnectionを処理できない
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する connectionごとの処理が完了しなければ次のconnectionを処理できない tokio::spawnによってグリーンスレッドを作成することでawaitを待たず に次のコネクションの処理に移るように
スレッドを立ててみる クライアントサーバー間でWebTransportコネクションを貼る 数百以上のクライアントが接続してくると想定する connectionごとの処理が完了しなければ次のconnectionを処理できない tokio::spawnによってグリーンスレッドを作成することでawaitを待たず に次のコネクションの処理に移るように
スレッドを立ててみる WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別のメッ セージが送信される
スレッドを立ててみる WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別のメッ セージが送信される
スレッドを立ててみる WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別のメッ セージが送信される
スレッドを立ててみる WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別のメッ セージが送信される while文によってloop処理になっているため片方のhandle_関数が動き始めるとも う片方が動かなくなる
スレッドを立ててみる WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別のメッ セージが送信される while文によってloop処理になっているため片方のhandle_関数が動き始めるとも う片方が動かなくなる これも別スレッドでloopさせるように変更
ここまでで立てたスレッド
ここまでで立てたスレッド WebTransportコネクションを待ち受け、Streamをそれぞれ別のスレッドで処理
スレッドを立ててみる ここまでで、中継サーバーの要件である3つのうち、2つまで完了
スレッドを立ててみる ここまでで、中継サーバーの要件である3つのうち、2つまで完了 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別 のメッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセ ージを転送する
スレッドを立ててみる ここまでで、中継サーバーの要件である3つのうち、2つまで完了 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別 のメッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセ ージを転送する
ここまでで、中継サーバーの要件である3つのうち、2つまで完了 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別 のメッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセ ージを転送する スレッドを立ててみる
ここまでで、中継サーバーの要件である3つのうち、2つまで完了 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別 のメッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセ ージを転送する スレッドを立ててみる
ここまでで、中継サーバーの要件である3つのうち、2つまで完了 クライアントサーバー間でWebTransportコネクションを貼る WebTransportコネクションの中で、2種類のストリームが開かれ、それぞれ別 のメッセージが送信される メッセージの種類に応じて、必要なものは送信者以外のクライアントにメッセ ージを転送する スレッドを立ててみる メッセージを受け取り、別のクライ アントの双方向ストリームを使って 送信したい
チャネルを使ってスレッド間通信 スレッド間でデータをやり取りして処理を行うために、Rustのtokioなどはチャネ ルという概念を用意されている
チャネルを使ってスレッド間通信 スレッド間でデータをやり取りして処理を行うために、Rustのtokioなどはチャネ ルという概念を用意されている スレッド間でメッセージを送信するためのパターンであり、 senderとreceiverのペアで構成される
スレッド間でデータをやり取りして処理を行うために、Rustのtokioなどはチャネ ルという概念を用意されている スレッド間でメッセージを送信するためのパターンであり、 senderとreceiverのペアで構成される sender / receiverはそれぞれ複数ある場合も 1対1: tokio::sync::oneshot N対1:
tokio::sync::mpsc チャネルを使ってスレッド間通信
スレッド間でデータをやり取りして処理を行うために、Rustのtokioなどはチャネ ルという概念を用意されている スレッド間でメッセージを送信するためのパターンであり、 senderとreceiverのペアで構成される sender / receiverはそれぞれ複数ある場合も 1対1: tokio::sync::oneshot N対1:
tokio::sync::mpsc チャネルを使ってスレッド間通信
チャネルを使ってスレッド間通信 スレッド間通信を実現するためにmpscが利用可能
チャネルを使ってスレッド間通信 スレッド間通信を実現するためにmpscが利用可能 スレッドAとスレッドBで通信する際には、senderとreceicerをそれぞれのスレッド に渡してあげれば良い
チャネルを使ってスレッド間通信 スレッド間通信を実現するためにmpscが利用可能 スレッドAとスレッドBで通信する際には、senderとreceicerをそれぞれのスレッド に渡してあげれば良い しかし、スレッドが増えてくると問題が発生する
チャネルを使ってスレッド間通信 スレッド間通信を実現するためにmpscが利用可能 スレッドAとスレッドBで通信する際には、senderとreceicerをそれぞれのスレッド に渡してあげれば良い しかし、スレッドが増えてくると問題が発生する
スレッド間通信を実現するためにmpscが利用可能 スレッドAとスレッドBで通信する際には、senderとreceicerをそれぞれのスレッド に渡してあげれば良い しかし、スレッドが増えてくると問題が発生する コミュニケーションパスが爆発する N*(N-1) チャネルを使ってスレッド間通信
スレッド間通信を実現するためにmpscが利用可能 スレッドAとスレッドBで通信する際には、senderとreceicerをそれぞれのスレッド に渡してあげれば良い しかし、スレッドが増えてくると問題が発生する コミュニケーションパスが爆発する N*(N-1) なので、管理するためのハブを用意する チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する このdispatcherはWebTransportコネクションの中で双方向ストリームが開く たびに追加されるため、一番最初に初期化しておく チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する このdispatcherはWebTransportコネクションの中で双方向ストリームが開く たびに追加されるため、一番最初に初期化しておく handle_connectionの中でdispatcherへの登録と受け取り処理を実装 チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する このdispatcherはWebTransportコネクションの中で双方向ストリームが開く たびに追加されるため、一番最初に初期化しておく handle_connectionの中でdispatcherへの登録と受け取り処理を実装 チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する このdispatcherはWebTransportコネクションの中で双方向ストリームが開く たびに追加されるため、一番最初に初期化しておく handle_connectionの中でdispatcherへの登録と受け取り処理を実装 チャネルを使ってスレッド間通信
send_streamを管理するdispatcherを定義 双方向ストリームが初期化されるごとに、mpscのsenderを登録し、必要になった らそれを使って送信する このdispatcherはWebTransportコネクションの中で双方向ストリームが開く たびに追加されるため、一番最初に初期化しておく handle_connectionの中でdispatcherへの登録と受け取り処理を実装 チャネルを使ってスレッド間通信
チャネルを使ってスレッド間通信 最終的にはマルチスレッドで以下のような形で処理
チャネルを使ってスレッド間通信 最終的にはマルチスレッドで以下のような形で処理
チャネルを使ってスレッド間通信 最終的にはマルチスレッドで以下のような形で処理 ①クライアントからメッセージを受け取る
チャネルを使ってスレッド間通信 最終的にはマルチスレッドで以下のような形で処理 ①クライアントからメッセージを受け取る ②マネージャースレッドにmpsc経由でメッセー ジを送り、送りたいクライアントが動作している スレッドへのSenderを入手する
チャネルを使ってスレッド間通信 最終的にはマルチスレッドで以下のような形で処理 ①クライアントからメッセージを受け取る ②マネージャースレッドにmpsc経由でメッセー ジを送り、送りたいクライアントが動作している スレッドへのSenderを入手する ③Receiverから受け取ったメッセージをクライア ントに送信する
マルチスレッドの難しさとまとめ ~本セッションのコードの振り返り~
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい 管理スレッドを立ててmpscでやり取りする Mutexで複数スレッドから操作できるようにする
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい 管理スレッドを立ててmpscでやり取りする デッドロックは起きないがmpscの管理が複雑 Mutexで複数スレッドから操作できるようにする
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい 管理スレッドを立ててmpscでやり取りする デッドロックは起きないがmpscの管理が複雑 Mutexで複数スレッドから操作できるようにする
デッドロックのリスク
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい 管理スレッドを立ててmpscでやり取りする デッドロックは起きないがmpscの管理が複雑 Mutexで複数スレッドから操作できるようにする
デッドロックのリスク スレッドで行われる処理や扱うデータのサイズなどを考慮して設計する必要が ある
マルチスレッドの難しさ スレッドを立てて個別の処理をすることは簡単 その処理の特性(CPU heavyかI/O heavy)でOSスレッド・グリーンスレッド の使い分けは必要 複数スレッドから参照するデータをどう扱うか?が難しい 管理スレッドを立ててmpscでやり取りする デッドロックは起きないがmpscの管理が複雑 Mutexで複数スレッドから操作できるようにする
デッドロックのリスク スレッドで行われる処理や扱うデータのサイズなどを考慮して設計する必要が ある 今回はマネージャー用のスレッドを立てたが負荷が偏ったら複数スレッドか ら操作できるようにMutex化するなどもありうる
終わり X: @yuki_wtz (Follow me!)