2019-09-28に更新

Rustの並列処理を学ぼうぜ☆(^~^)?<その1>

読了目安:29分

20190923comp30a1.png

シングル・コアからの卒業☆(^~^)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Rust でゲーム・サーバーを作ろうと思っても、あれができない、これができない、と すぐ行き詰ってしまう……☆
古い知識のアップデートが必要だぜ☆」

Rust はどのようにして安全な並列処理を提供するのか
並行性

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 通信処理は、もっと整理された枠組みの 並列処理(Parallel processing) の一部になっているようだな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 シングル・コアのプログラミングからは 今日から卒業か☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 シングル・プロセッサ、 マルチ・コア、 マルチ・スレッド なの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そうそう……、おさらいしようぜ☆
特に 並列性(へいれつせい; ペアラレズン; parallelism) と 並行性(へいこうせい; クンコレンシー; concurrency) は なんど読んでも見間違うぜ☆」

並列性はペアラレズン(parallelism)☆ シングル・プロセッサ で マルチ・コア☆(^~^)

20190922math18a14b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 このアイコンは 産業革命頃の家かも知らないが とりあえず インターネットが普及した現代としよう☆」

20190922math18a14b2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 突如として 4つの家が 謎のコネクションを確立したとしよう☆」

20190922math18a14b3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 本来ばらばら だった この家みたいなアイコン…… Node(ノード)とでも言うかだぜ☆、
ノードが集まって 1つのことをしようとするコンピューターたちを Cluster(クラスター)と呼ぶ☆
この スケール の話しだと マルチ・プロセッサ になる☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それと もちろん……☆」

20190922math18a14b5.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ノード1つで クラスター と言い張ることも可能☆ リクツとしては……☆」

20190922math19a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 家に ↑3台 のPCがあり……☆」

20190922math19a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 突如として 3台のPCが 謎のコネクションを確立したとしよう☆」

20190922math19a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これも 本来ばらばら だった ノードが集まって 1つのことをしようとするので クラスター だぜ☆
この スケール でも マルチ・プロセッサ になる☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それと もちろん……☆」

20190922math19a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ノード1つで クラスター と言い張ることも可能☆ リクツとしては……☆」

20190922math20a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 1つのPCに 32bit CPUが2基 積んであって 64bit CPU……いや こんなPC聞いたことないぜ☆
セガ・サターンでしか聞いたことない☆
サーバー・マシンなら 電話ボックスみたいな大きな1台の箱の中に たくさん CPU が詰め込んであるものはあるが
CPU だけ詰め込んであっても嬉しくなくて 電源もファン(換気扇)もハードディスクも付いている……、
あれは ブレード・サーバー という鉄のタンスみたいな中に 8つぐらいPCを挿してるわけで PCの中にCPUが複数あるといえるかは分からない……☆
だいたい 廃熱音で耳やられるとか 個人が お目にかかることもない☆
今どきのPCは……☆、」

20190922math20a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 PCには1つのCPUが積んであって、1つのCPUが 複数のCPUのふり をしてくれる マルチコア だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 なんで 複数のCPUのふり ができるんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 小さく造って 詰め込んでいるからだぜ☆
光の速さは 1秒間に 地球を およそ7周半と決まっているように、CPUの仕事の速さは限界に達している☆
だから CPUのコア数 を増やす方向に発展してるんだぜ☆」

20190922math20a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これも 本来ばらばら だった ノードが集まって 1つのことをしようとするので クラスター だぜ☆
この スケール だと シングル・プロセッサ になる☆
CPUは Central Processing Unit の頭文字だからな☆」

20190922comp21a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クラスター使いの作業相手は ノード だからな☆ どんなスケールかは関心がないぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 お父んは クラスター のことを ぶどう か何かだと思ってるだろ☆」

20190922comp22a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ノードの数を減らすことを スケールイン(Scale in) 、ノードの数を増やすことを スケールアウト(Scale out) と言うそうだぜ☆」

20190922comp22a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ノードが使っているCPUの性能を下げることを スケールダウン(Scale down) 、性能を上げることを スケールアップ(Scale up) と言うそうだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ここまでの説明で ひとまず、 シングル・プロセッサ で マルチ・コア なのは PCの教養 みたいなもので 明らかに区別できるだろう☆
次☆」

並行性はクンコレンシー(concurrency)☆ マルチ・ジョブと マルチ・タスクと マルチ・プロセスと マルチ・スレッド☆(^▽^)

20190922math18a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コンピューターから見れば 人間はターミナルだし、
人間から見れば コンピューターはターミナルだぜ☆」

20190922math18a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 人間は ジョブ をこなすために コンピューターを使う☆」

20190922math18a3.png

20190922math18a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 シングル・ジョブとか、 マルチ・ジョブ といった言葉は聞いたことがないぜ☆
なぜなら マルチ・ジョブ が あたりまえ だからだぜ☆」

20190922math18a5.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 で、1時間に1つずつ ジョブをこなしていくのなら、 シングル・タスク だぜ☆」

20190922math18a6.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 いや、一度に3つこなせる、というのなら、 マルチ・タスク だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 なんで 3時間 かかっていたことを 1時間 で できるんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 作業を共通化して1回分にしたり、 片手が空いていたときに 他にできることを やってしまったり、
連絡したのに 返事が返ってこない待ち時間に 他の作業をしたりと
時間あたりのCPU利用率を上げていくわけだな☆ それと……☆」

20190922math18a7.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 作業を中断したり、再開する機能が OS(Operating system; オペレーティング・システム) に付いている☆
時間がかかっている作業は yield(イヨード; 譲る)して 別の作業に時間を渡すぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 プログラマーが考えなくても オペレーティング・システムが ワーク・シェアリング(作業の配分) を やってくれるわよね。楽よね~」

20190922math18a8.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Aさんは作業を指示しただけで、実際働いてるのはCPUなんで☆
シングル・コア から見れば ↑こんな図だぜ☆」

20190922math18a9b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・コア になれば そりゃ 速いよな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 司令塔のマスターと 駒のスレーブとの連携がヘタクソだったり、
せっかく並列計算しても コア間で データの受け渡しが遅くて オジャンになったり、
1つのコアだけ早く仕事が終わって遊んでたりすると 結局 トントン になるんだがな☆」

20190922comp18a10b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 人間から見ると ジョブ、
コア から見ると プロセス(Process) に名前を変えるが……☆、」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なんで名前を変えたの?」

20190922comp24a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 人間は 大雑把に指示を出すが、 その指示は 言ってない多くのことを含むんだぜ☆
ピザ屋は ピザ配ってんのかというと 生地を伸ばすこともすれば 焼くこともしているだろ☆ 見えないプロセスがあるんだぜ☆
でも ジョブは ピザ屋 だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ジョブは 大雑把すぎるんだぜ☆ 人間に向けた ラベル のようなもの☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ぬぐぐぐぐ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 もう少し ジョブ を明らかにしておくかだぜ☆」

20190922comp25a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Enter キーを押すまでに 伝えた内容が ジョブ だぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ジョブ(仕事)に エンター(入る) するキー(ボタン)だったのね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コンピューターは 1つのジョブを 複数のプロセスを 順番に行うが、これは 連続して行う1つずつのプロセスだぜ☆
マルチ・プロセスとは呼ばない☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 もし マルチ・プロセス という用語が出た場合は、同時並行しているプロセスのことだと思えだぜ☆」

20190922math18a10.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 話しを戻す☆
プロセスは、さらに スレッドという単位に分けて、時間を振り分けている☆
yield で時間を譲るのは実際には スレッド単位で行われる☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 CPU は Central Processing Unit の頭文字なんで、
コアが扱っているのが Process(プロセス) なのは 分かるだろ☆
で、このとき……☆」

20190922math18a12.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 全部のスレッドが、同じ CPU を使っているなら
シングル・コア、 マルチ・スレッド プログラミング だぜ☆」

20190922math18a13b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 1つのプロセスの いずれかのスレッドを 別の コア に振り分けてこそ マルチ・コア プログラミング だよな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 マスターで1個使ってるの もったいないよな☆ マスターも働けよ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 プロセスの中で なんで スレッドに分かれて、 スレッドは 何やってるのかと言うと……☆」

20190922math18a11.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 人類が カタカタ とキーボードを打っている間の退屈な待ち時間、
せわしなく ディスプレイに向かって バスに乗って画像を転送している時間、
インターネットからファイルをダウンロードしている待ち時間、
いろんな相手との間で 待ち をするからだぜ☆ 待ち の間に何もできないのが もったいないので スレッドで分けている☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 スレッドとは 言ってしまえば ある種のチームを組んだもので、 待ち時間になったら譲る ことを決めたメンバーだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 マルチ・コア・プログラミングも OS が勝手に やってくれないのかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・コア にした方が 作業が速く終わるのか、 シングル・コア にした方が 作業が速く終わるのか
オペレーティング・システムは 知ってないので、やってくれないぜ☆」

マルチ・スレッド・プログラミング(並行性) vs マルチ・コア・プログラミング(並列性)☆(^◇^)

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 マルチ・スレッド・プログラミングと マルチ・コア・プログラミング には そんなに差があるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・スレッド・プログラミングは人類には早すぎた と言うプログラマーがいたり、
PCはマルチ・コアになってから何十年も経つのに、ほとんどのソフトウェアはマルチ・コア・プログラミングで書かれていない
という意見も目にするぜ☆」

20190922comp23a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・コア・プログラミング というのは言ってしまえば マルチ・スレッド・プログラミング の特殊な例の1つに過ぎないわけだが、
コアとは結局 CPUのふりをしているものだから CPU と 同じと考えれば、
突き詰めれば シングル・プロセッサーと マルチ・プロセッサー の違いだぜ☆ 何が違うか 考察してみようぜ☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 シングル・プロセッサー には 何か大きな利点があるはずだぜ☆ マルチ・プロセッサーにない大きな利点が☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 東京23区内暮らしか、一番近いコンビニが10km先とか それぐらいの違いはあるだろうな☆」

20190922comp23a2b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 じゃあ ちょいと 隣のノードに データを渡してくれだぜ☆ ちょっと使うんで☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 CPUのコアは CPUの中にあるから すぐ隣だけど、
インターネットにつながっている お隣の家って どこにあるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 メキシコかも知れないな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 光を頑なに拒み、ADSL回線を使い続けているノードだったら どうすんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そんなノードは 早く通信切断した方がいいかもしれないな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Windowsアップデートで ノードのPCが再起動されたら どうすんの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コネクションは切断されるな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 keep alive☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そんなメッセージは要らないんで☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 通信が失敗することを考えながら プログラムを組まなくちゃいけなくない?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 やる気が沸いてきただろ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 マルチ・コア・プログラミングは、 通信プログラム なのかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クラスターやるなら ノードが メキシコ にあるのか CPUの中にあるのか 関心がないし、
並列処理 するのなら うまみ は 複数台のPC を使うことだろ☆
マルチ・コア・プログラミング やるのは 通信プログラミング やることに違いないぜ☆」

マルチ・スレッド・プログラミング(並行性)は人類には早すぎた☆m9(^▽^)

C++ マルチスレッド 入門
マルチスレッドプログラムのバグ
マルチコア向けソフトウェア開発/デバッグの基礎と実際

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・スレッド・プログラミングが なぜ 人類には早すぎた と言われるのか、
これは 他のプログラム言語でも言えることだから、↑先人が書き残したものを当たろうぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 何が そんなに嫌われているの?
プログラミングのテクニックで 楽にできないの? プログラマーは無敵じゃないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 一番よく耳にするのは、
シングル・スレッドのプログラムでは起こらなかったことが、マルチ・スレッドのプログラムでは起こる
からだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 気をつけろだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 たった2つ のことなんだぜ☆ その2つのことを扱うには、人類には早すぎた☆ 例を示そう☆」

人類には早すぎた並行性 その1 Data race

20190922comp26a1.png

館内放送「 ホワイト・ボードに書かれている数を 1大きい数に 書き直してください」

KIFUWARABE_80x100x8_01_Futu.gif
「 12だろ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 やってみろだぜ☆」

20190922comp26a1b1.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ほぼ同時に立ち上がったぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 どっちが早く立ち上がろうとも 結局 12 なんじゃないの?
というか 片方が立ち上がったんなら もう片方は 立ち上がらなくてよくない? 何しにホワイトボードに向かうの?

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・コア・プログラミング(並列性)の話しでもあることに 気をつけてくれだぜ☆」

20190922comp26a1b2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今 片方は 飛行機に搭乗したところかも知れない☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 こんな 単純なジョブで パシるのがいけないんだぜ☆」

20190922comp26a1b3.png

KIFUWARABE_80x100x8_01_Futu.gif
「 よし、12に 書き換えたぜ☆ 黄緑さんは 仕事をした☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ホワイトボードに書かれている数 って言ってたけど、
最初にみた数と、書き直された数の どっちを指しているの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ジョブを受けてから、最初にホワイトボードを 見たときの数だぜ☆」

20190922comp26a1b4.png

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 腹いせか知らないけど 上から 12 で書き殴ったわよ!」

KIFUWARABE_80x100x8_01_Futu.gif
「 やったな!」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今回は やったかもしれない☆ しかし……☆、」

20190922comp26a1b5.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今、メキシコから間に合った 桃色さん の場合はどうだろう☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 そんな 歴史のif 見たくないのよ!」

20190922comp26a1b6.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ジョブを受けて 初めてホワイトボードを 見た時の数より 1大きくしたぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 なぜ 東京23区内の桃色さんと メキシコの桃色さんで 結果が変わってしまったのだろうか……☆?
プログラマーは どちらの結果を望んで 館内放送を出したんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 何にも考えずに 館内放送を出したんだろ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ノードの都合で 結果が変わるんなら、プログラミングなんて 成り立たなくない?!

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 しかし 都合がつかずに動けない方は後回しにして 動けるやつに どんどん仕事してもらいたい のが
そもそもの マルチ・スレッド(並行性) の意義だぜ☆
ノードの都合で 行動は変わって欲しいし、結果は変わってほしくないんだぜ☆

KIFUWARABE_80x100x8_01_Futu.gif
「 タイミング などという陳腐な理由で プログラミングの手順を入れ替えられてしまったら、プログラミングの概念が崩れる……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 シングル・スレッド・プログラマーは まず びっくりするだろ☆
プログラムの経験があるから大丈夫……、という その経験から 真っ先にウソを付くぜ☆」

人類には早すぎた並行性 その2 Dead lock

20190923comp27a1.png

館内放送「2つのボールを自分のポケットに入れてください。2つとも入れたら、2つとも台に戻してください」

KIFUWARABE_80x100x8_01_Futu.gif
「 簡単だろ☆ 入れろだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 やってみろだぜ☆」

20190923comp27a2.png

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 だから何で 2人が立つの!
片方が立ち上がったんなら もう片方は 立ち上がらなくてよくない? 何しにボールに向かうの?!

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 マルチ・コア・プログラミング(並列性)の話しでもあることに 気をつけてくれだぜ☆」

20190923comp27a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今 片方は 飛行機に搭乗したところかも知れない☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 学習しなさいよ! 歴史のelse は繰り返したくないのよ!」

20190923comp27a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 よし、2人とも 1つ目のボールを 自分のポケットに入れたな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 これは よくないんじゃないか☆?」

20190923comp27a5.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ささ、2つ目のボールをよこせだぜ☆ 3分間だけ待ってやろう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 お前が よこせだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 デッドロック してんじゃないわよ!」

20190923comp27a6.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 桃色さんが 遅れてやってきてくれれば 何も問題はなかった……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 遅れてくるとか 遅れてこないとか、 タイミングでデッドロック してんじゃないわよ!」

人類には早すぎた、割り込みする並行処理

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 データレースも、デッドロックもそれぞれ 根本的な解決から 対処的な解決まで
さまざまな事態に向けて リーズナブルに合わせた個々の解決方法が すでに知られている☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 じゃあ解決しろだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 その個々の解決方法が、 シングル・スレッド・プログラミングを使った方がマシかも であったなら、
小さな問題は解決したけど 大きな問題は解決していない、
マルチ・スレッド・プログラミング(並行性)の解決方法にはならない ことは ご理解いただけるかだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 タイミングで崩れたりしない シングル・スレッド・プログラミング って、すごくない?
プログラミングのすごさの9割9分の理由は 割り込み を気にしなくていいことなんじゃないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 割り込み を気にしなくていいなら、むしろ マルチ・スレッド・プログラミング や
マルチ・コア・プログラミングを 最大限に活かせる場面だぜ☆ 言ってしまえば……☆」

20190923comp28a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 従業員が2人いて 同時並行で働いていれば マルチ・スレッド だぜ☆
片方は 車を設計していて、 片方は 税金を計算している☆
ただし、自分にやることがなくなっても 隣の仕事を横取りできない☆ 専門職だから、割り込めないぜ☆
これは ある意味 並行処理の完成形その1 ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 マルチ・ユーザー という方が一般的だよな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そもそも 何の縁もなく 割り込みもない人たちが 世界中のどこかで働いていれば 並行処理なわけだぜ☆
しかし わたしたちは、 割り込みする並行処理 をしたいわけだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ひとり山奥にこもって ろくろを回しながら 土器 作ってるわけにも いかないですもんね」

20190923comp30a2.png

KIFUWARABE_80x100x8_01_Futu.gif
「 コミュニケーション能力 があれば みんなで割り込んでも 大丈夫だろう☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 例えば 水色のボールを先にポケットに入れ、オレンジ色のボールを次にポケットに入れる というルールを
徹底すれば このケースでは デッドロックしない……☆
でも 自信満々のおっさんや 若いのが現れて
『だいじょーぶ、だいじょーぶ』と言いながら 軽くひょいっと 俺流 で オレンジ色のボールを
先にポケットに入れて デッドロック するぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なんで 俺流 で やってしまうの?! つまみだせないの!?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ルールが整備されていないところで 発揮されるのが コミュニケーション能力 だろ☆
ルールが整備されているとは知らずに コミュニケーション能力 を発揮したんだろ☆
どちらかというと コミュニケーション能力 が無く ルール通りに動くロボットの方が失敗しない☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 まあ ルール通り動いて 水色のボールをポケットに入れた瞬間 ぽっくり逝ってしまう爺さんも いるかも知れない☆ デッドロックする☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ぽっくり逝って 強制終了するのは シングル・スレッド・プログラミングでも同じだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 どんどん 割り込み いらっしゃい、というプログラムを組むことも ある意味、
並行処理の完成形その2
これは 何も書き込んだりしない 読み取り専用プログラムなら できる……☆」

電卓から機械学習へ

20190923comp31a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 昔のコンピューターは、完全解明されていることを 計算するものだった☆
計算結果は 正解だぜ☆」

20190923comp31a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 2019年現在のコンピューターは、それっぽい答えを当てにいくものだぜ☆
計算結果は 多分 いい感じのものかもしれないぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 これは 劣化しちゃったの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 限られた範囲の中で正しい答えを返すコンピューターは、
限られた範囲の外へ出て 応用できる範囲を広げたという意味で、大きな進歩だぜ☆
人類の 制度 の方が コンピューターの進めた一歩に 付いてこれてないだけで☆」

20190923comp31a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そして コンピューターによって 計算結果が異なる ということが 起こり始めた☆
コンピューターへの関心は 答え から、 精度 へ 移ったんだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 この変化が、 マルチ・コア・プログラミング(並列性) にとって 良い変化だったわけだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 なんでだぜ☆?」

20190923comp31a1b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 昔のコンピューターは 計算にかかる時間 が能力の目安だったんだぜ☆
速く計算が終われば 性能が高い☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 では、計算にかかる時間を縮めることが評価されるとして、
マルチ・コア・プログラミング にすることで、
コアの数を 増やせば増やすほど、 計算にかかる時間を縮められるか☆?」

20190923comp32a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これには アダムール が コアの数を増やしても計算にかかる時間は縮まらなくなると 悲観的な法則を示している☆
60分かかる仕事のうち 並列処理できるのが57分で、残りの3分は 分配できないとしよう☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 じゃあ この仕事は コアを何個積んでも 3分より短くならない☆
3分は20分の1だから、この時点で、20倍を超える高速化は できないことが確定する☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 57÷3=19 だから、全部で20コアあれば 最速の3分ね。
21コアにしても 誰にでも振り分けられる仕事が 57÷20 = 2.85分 で終わるだけで、
並行できない仕事の 3分 は 必ず かかってしまうわよ」

KIFUWARABE_80x100x8_01_Futu.gif
「 全体の5% が並列化できない仕事は、21コア以上に増やしても 無意味かだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 計算にかかる時間 が能力の目安だった頃のコンピューターなら そうだな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 機械学習の頃のコンピューターの マルチ・コア・プログラミング なら
コア数を増やすことに どんな意味が出てくるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 同じ時間を掛けても、精度が上がる☆」

Rustの "大胆不敵な並行処理" - Fearless Concurrency

並行性
Rust by Example
Fearless Concurrency

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 まず 並行処理 を理解しようぜ☆ そのあと 並列処理 を考えればいい☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 データ・レースも デッド・ロックも無くなるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 データ・レース ができるようなプログラムは書けない、とのことだぜ☆
デッド・ロックはする☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 とりあえず スレッドを作るプログラムを書いてみようぜ☆」

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo run
 * 
 * [並行性](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/concurrency.html)
 */

use std::thread;

fn main() {
    thread::spawn(|| {
        println!("Hello from a new thread!");
    });

    println!("Hello from a main thread!");
}

Output:

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust> cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1    
PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\ep1.exe`
Hello from a main thread!
thread '<unnamed>' panicked at '
PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1>

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 早くも エラー落ちか~☆」

20190923comp33a1.png

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\ep1.exe`
Hello from a main thread!
Hello from a new thread!

KIFUWARABE_80x100x8_01_Futu.gif
「 どうやって 直したんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 実行すると、うまく行くときと、うまく行かないときがある☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 何を勉強してきたんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これは、スレッドを作成する前に、メイン・スレッドが終わってしまっているんだぜ☆
メイン・スレッドが すぐ終わらないよう、20ミリ秒 待たせてみようぜ☆?」

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo run
 * 
 * [並行性](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/concurrency.html)
 */

use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        println!("Hello from a new thread!");
    });

    println!("Hello from a main thread!");
    thread::sleep(Duration::from_millis(20));
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 よし、動作が安定した……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ぼんやりしたプログラミングねぇ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 スレッドは値を返すことも できるようだぜ☆ やってみようぜ☆」

20190923comp33a2.png

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo run --example return
 * 
 * [並行性](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/concurrency.html)
 */

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        "Hello from a new thread!"
    });

    println!("Main            | Child said: {}", handle.join().unwrap());
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example return
   Compiling ep1 v0.1.0 (C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.52s
     Running `target\debug\examples\return.exe`
Main            | Child said: Hello from a new thread!

KIFUWARABE_80x100x8_01_Futu.gif
「 .join() メソッドが スレッドが終わるまで待っているのかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 .join() はそういう意味だと思うが……☆」

Test

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo build --example data-race-1
 * cargo run --example data-race-1
 * 
 * [並行性](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/concurrency.html)
 */

use std::thread;
use std::time::Duration;

fn main() {
    let mut data = 1;

    let handle = thread::spawn(move || {
        data += 1;
        println!("Child           | data: {}", data);
    });

    handle.join().unwrap();
    println!("Main            | data: {}", data);
    thread::sleep(Duration::from_millis(50));
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example data-race-1       
   Compiling ep1 v0.1.0 (C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.41s
     Running `target\debug\examples\data-race-1.exe`
Child           | data: 2
Main            | data: 1

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 子スレッドの中の data はコピーなのだろうか☆ メイン・スレッドに影響がないぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 親スレッドから 子スレッドへ 初期値を渡せるだけで、
子スレッドの中で変更しても 親スレッドには影響がないんじゃないか☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 子スレッドで行った変更を、親スレッドが知ることができなかったら、嬉しくないんじゃない?」

KIFUWARABE_80x100x8_01_Futu.gif
「 子スレッドが終わるまで .join() で待って 返り値を取ったらどうだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ゲーム・サーバーに向いてなくない?」

メッセージ受け渡しを使ってスレッド間でデータを転送する

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 チャネル というのがあるらしい☆ 調べてみようぜ☆?」

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo build --example channel
 * cargo run --example channel
 * 
 * [メッセージ受け渡しを使ってスレッド間でデータを転送する](https://doc.rust-jp.rs/book/second-edition/ch16-02-message-passing.html)
 */

use std::thread;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    // 値は{}です
    println!("Main            | Got: {}", received);
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example channel
   Compiling ep1 v0.1.0 (C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.83s
     Running `target\debug\examples\channel.exe`
Main            | Got: hi

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 子スレッドから、親スレッドに 値を返すことは できるみたいだな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ずっと 往復できんの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 試しに ベタ書き してみるかだぜ☆」

20190923comp33a3.png

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo build --example channel-2
 * cargo run --example channel-2
 * 
 * [メッセージ受け渡しを使ってスレッド間でデータを転送する](https://doc.rust-jp.rs/book/second-edition/ch16-02-message-passing.html)
 */

use std::thread;
use std::sync::mpsc;

fn main() {
    let (childEnter, childExit) = mpsc::channel();
    let (mainEnter, mainExit) = mpsc::channel();

    let childHandle = thread::spawn(move || {
        let mut childBall = 1;
        childEnter.send(childBall).unwrap();

        childBall = mainExit.recv().unwrap();
        println!("Child           | Expected: 11, Got: {}.", childBall);
        childBall += 100;
        childEnter.send(childBall).unwrap();

        childBall = mainExit.recv().unwrap();
        println!("Child           | Expected: 1111, Got: {}, Finished.", childBall);
    });

    let mut mainBall = childExit.recv().unwrap();
    println!("Main            | Expected: 1, Got: {}.", mainBall);
    mainBall += 10;
    mainEnter.send(mainBall).unwrap();

    mainBall = childExit.recv().unwrap();
    println!("Main            | Expected: 111, Got: {}.", mainBall);
    mainBall += 1000;
    mainEnter.send(mainBall).unwrap();

    childHandle.join().unwrap();
    println!("Main            | Finished.");
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example channel-2
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target\debug\examples\channel-2.exe`
Main            | Expected: 1, Got: 1.
Child           | Expected: 11, Got: 11.
Main            | Expected: 111, Got: 111.
Child           | Expected: 1111, Got: 1111, Finished.
Main            | Finished.

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 キャッチボールできてるような結果は返してきているぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 子スレッドが2つでも いけるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ベタ書きで 試してみるぜ☆」

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo build --example channel-3
 * cargo run --example channel-3
 * 
 * [メッセージ受け渡しを使ってスレッド間でデータを転送する](https://doc.rust-jp.rs/book/second-edition/ch16-02-message-passing.html)
 */

use std::thread;
use std::sync::mpsc;

fn main() {
    // 2つの方向。
    let (child1Enter, child1Exit) = mpsc::channel();
    let (child2Enter, child2Exit) = mpsc::channel();
    let (mainEnter1, mainExit1) = mpsc::channel();
    let (mainEnter2, mainExit2) = mpsc::channel();

    let child1Handle = thread::spawn(move || {
        let mut childBall = mainExit1.recv().unwrap();
        println!("Child1          | Expected: 1, Got: {}.", childBall);
        childBall += 10;
        child1Enter.send(childBall).unwrap();

        childBall = mainExit1.recv().unwrap();
        println!("Child           | Expected: 11111, Got: {}, Finished.", childBall);
        childBall += 100000;
        child1Enter.send(childBall).unwrap();
    });

    let child2Handle = thread::spawn(move || {
        let mut childBall = mainExit2.recv().unwrap();
        println!("Child           | Expected: 111, Got: {}.", childBall);
        childBall += 1000;
        child2Enter.send(childBall).unwrap();

        childBall = mainExit2.recv().unwrap();
        println!("Child           | Expected: 1111111, Got: {}, Finished.", childBall);
        childBall += 10000000;
        child2Enter.send(childBall).unwrap();
    });

    let mut mainBall = 1;
    mainEnter1.send(mainBall).unwrap();

    mainBall = child1Exit.recv().unwrap();
    println!("Main            | Expected: 11, Got: {}.", mainBall);
    mainBall += 100;
    mainEnter2.send(mainBall).unwrap();

    mainBall = child2Exit.recv().unwrap();
    println!("Main            | Expected: 1111, Got: {}.", mainBall);
    mainBall += 10000;
    mainEnter1.send(mainBall).unwrap();

    mainBall = child1Exit.recv().unwrap();
    println!("Main            | Expected: 111111, Got: {}.", mainBall);
    mainBall += 1000000;
    mainEnter2.send(mainBall).unwrap();

    mainBall = child2Exit.recv().unwrap();
    println!("Main            | Expected: 11111111, Got: {}.", mainBall);

    child1Handle.join().unwrap();
    child2Handle.join().unwrap();
    println!("Main            | Finished.");
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example channel-3
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target\debug\examples\channel-3.exe`
Child1          | Expected: 1, Got: 1.
Main            | Expected: 11, Got: 11.
Child           | Expected: 111, Got: 111.
Main            | Expected: 1111, Got: 1111.
Child           | Expected: 11111, Got: 11111, Finished.    
Main            | Expected: 111111, Got: 111111.
Child           | Expected: 1111111, Got: 1111111, Finished.
Main            | Expected: 11111111, Got: 11111111.        
Main            | Finished.

20190923comp33a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 親1つ、子2つでも キャッチボールできてるような 結果は返してきているぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 じゃあ コンピューター囲碁サーバーでも、コンピューター将棋サーバーでも 作れるな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これはまだ マルチ・スレッド・プログラミング(並行性) だぜ☆
マルチ・コア・プログラミング(並列性) をするには 通信も必要だぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 TCP/IP 通信をちらっと見返してきたら むずかしかったんで、
もう少し マルチ・スレッド・プログラミングを練習しようぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 じゃあ 自然数の偶数 を打鍵したら 子スレッド1が 2倍した数を メッセージとともに返してきて、
自然数の奇数 を打鍵したら 子スレッド2 3倍した数を メッセージとともに返してくるように
しましょう」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 標準入力から打鍵した文字列を取得するのは コンピューター将棋で作ったことがあるぜ☆」

Test:

/**
 * cargo new ep1
 * cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1
 * cargo check --example input
 * cargo build --example input
 * cargo run --example input
 */
use std::io;

fn main() {
    let mut line = String::new();

    // コマンド プロンプトからの入力があるまで待機します。
    io::stdin()
        .read_line(&mut line)
        .expect("info Failed to read_line"); // OKでなかった場合のエラーメッセージ。

    // 末尾の 改行 を除きます。前後の空白も消えます。
    line = line.trim().parse().expect("info Failed to parse");

    println!("Read            | {}", line);
}

Output:

PS C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1> cargo run --example input
   Compiling ep1 v0.1.0 (C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\concurrency\ep1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s
     Running `target\debug\examples\input.exe`
hello
Read            | hello

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 このブログが重くなってきたから 次の記事に移ろうぜ☆」

次の記事へ

ツイッターでシェア
みんなに共有、忘れないようにメモ

むずでょ

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント