BackEnd

goroutineとchannelとContext

投稿日:

はじめに

最近、Goを本格的に勉強しており、並行処理として使用機会も多く自分なりに理解が追いついていない goroutine と channel について勉強していきたいと思います。

並行処理と並列処理

goroutine と channel はgo言語の並行実行機能になります。並行処理と並列処理の違いは以下になります。

  • 並行(concurrent)処理とは、複数のタスクを非同期で同時に実行すること。
  • 並列(parallel)処理とは、複数のCPUを使って複数のタスクを同時に実行すること。

相互補完的な概念ですが、同じものではありません。並行性は複数のタスクが同じ時間帯に開始され、処理を実行し、終了することをさし、タスク間の相互作用が可能です。
並列性の場合は、単に複数のタスクが同時に実行されているだけになります。

goroutine

goroutine とは並行処理を実現するための軽量なスレッドのようなのもので、関数やメソッドの前に go をつけると goroutine として実行されます。

例えば、上記のコードを実行すると何も出力されません。これは goroutine の実行より先にプログラムが終了するからです。

“goroutine” と表示されるはずです。 main 関数中で1秒スリープしているため、 goroutine がプログラムの終了より先に実行されます。
goroutine 実行すると、内部ではそれをスケジューリングし実行します。複数の goroutine を実行すると、それらはキューに積まれて処理されていきます。そのため、最初の例のように、処理が goroutine に切り替わる(スイッチング)より先に main 関数が終了する場合があります。 goroutine の終了を待つのに、 sleep で goroutine の終了を待つ、 sync.WaitGroup で同期をする等、処理の流れを制御する必要があります。

ただし、 goroutine だけでは並行処理のタスク間の相互作用ができません、そこで使用するのが channel になります。

channel

goroutine 間でデータを送受信するためのもので、 channel のサイズ ( buffer )で、1度に扱えるデータのサイズを指定できます。
channel は、 goroutine 間でデータの通信を同期して処理するための配列のようなものです。容量なしの channel は unbuffered channel と呼ばれ、容量ありの channel は、 buffered channel と呼ばれます。

channelからデータ受信

このプログラムを実行すると、”data” と表示されます。前章までの流れだと、 goroutine より先に main 関数が終了するように思えますが、 channel を用いることで、 channel にデータを受信するまで処理が止まります。これはブロックと呼ばれ、 channel はデータを受信するまで処理をブロックする性質があります。そのため、この例では goroutine を実行してから main 関数が終了します。

for-range でのデータ受信

受信するデータの数がわからないときは、このように range 関数と channel を用いて、 goroutine を制御することもできます。

複数のchannelを受信

上記の例では channel のデータの数を制御しましたが、複数の channel を受信したい場合もあると思います。そんなときは、 select – case を用いることで複数の channel の受信を制御することができます。

buffered channel

channel は、 make 時に buffer (容量)を指定することができます。

unbuffer なchannel には、続けてデータを送信することはできません。 channel に空きがないときにデータを送信しようとするとブロックになってしまうからです。 size を指定した buffered channel は、その数だけデータをキューイングすることができます。上記のプログラムのように、データを2つバッファされ、続けてデータが受信されます。上記のプログラムを実行すると、 “buffered” 、 “channel” が順に出力されます。

例えば、 buffer がいっぱいになったとき、 buffered channel は、 unbuffered channel と良く似た動作をします。つまり、 channel に空きができるまでブロックが起こります。
下記の処理を実行すると、

以下のように出力されます。

ブロックが発生するまでに3つの数でいっぱいになっていることがわかると思います。

Context

並行処理のプログラムではタイムアウトやキャンセル、あるいはシステムの別の箇所での失敗により、しばしば中断する必要があります。
単純なキャンセルの通知に付随して追加の情報も伝達できると便利です。例えば、キャンセルが発生した理由や、関数の処理を終わらせるべきデッドラインがあるか、などです。
contextパッケージはそういった背景をもとに、標準ライブラリに追加され、これを並行なコードを扱う際に考慮すべき標準なGoイディオムとしました。
contextパッケージには2つの主な目的があります。

  • コールグラフの各枝をキャンセルするAPIを提供する。
  • コールグラフを通じてリクエストに関するデータを渡すデータの置き場所を提供する。

今回は前者の、キャンセルAPIの提供について触れていきたいと思います。

基本的な使い方

goroutine を呼び出す元の方でオブジェクトを生成します。 context.Background() は空のコンテキストを生成します。

これを goroutine に引き渡し伝搬させていきます。

WithCancel

手動でキャンセルする場合はWithCancelを使います。

WithTimeout

タイムアウトでキャンセルする場合はWithTimeoutをつかいます。

WithDeadline

WithDeadline は指定した時刻を超えると、処理がキャンセルされます。

Done

キャンセルされたかの判断はselectでコンテキストのDoneを待ちます。

タイムアウトのサンプル

上記を踏まえたタイムアウトのサンプルになります。

実行結果

さいごに

Go言語を使用するにあたって並行処理は必須かと思いますので、応用も含めて勉強していきたいと思います。 sync.WaitGroup や sync.Mutex は次回のブログで解説できればと思います。

おすすめ書籍

スターティングGo言語 (CodeZine BOOKS)  Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズ  改訂2版 みんなのGo言語

blog-page_footer_336




blog-page_footer_336




-BackEnd
-

執筆者:


comment

メールアドレスが公開されることはありません。

CAPTCHA


関連記事

rails

Railsでの非同期処理とDelayed Job

1 はじめに2 Active Job2.1 Active Jobの役割2.2 ジョブを作成する2.3 ジョブをキューに登録する2.4 コールバック2.5 例外3 Delayed Job3.1 設定3. ...

markdownで書けるドキュメントツールのGitbookを試す

1 はじめに2 Gitbookとは3 nvm4 node.jsインストール5 Gitbook導入5.1 インストール5.2 初期化5.3 ローカルでブラウザから確認6 作成と編集6.1 見出し編集7 ...

麻雀で自分が何を切るか学習させる

1 はじめに2 プロジェクト名を決めてみる3 何切る問題で考慮される要素3.1 手牌3.2 ドラ3.3 考慮していない要素(本当は入れたい)4 学習の方法5 さいごに はじめに 前回執筆しました、配牌 ...

rails

Shrineでアップロードする際に画像を加工する

1 はじめに2 アップロードする画像のリサイズ2.1 Gemを追加2.2 Uploaderの修正3 サムネイルを作成する3.1 Uploaderの修正3.2 サムネイルを表示する4 バリデーションの追 ...

rails

関連するモデルのレコードを一緒に作成する方法

1 はじめに1.1 前提条件2 実装2.1 モデルの作成2.2 コントローラの作成2.3 Viewの作成3 さいごに はじめに フォームからレコードを作成する際に、関連するモデルのレコードを一緒に作成 ...

フォロー

follow us in feedly

blog-page_side_responsive

2020年4月
 1234
567891011
12131415161718
19202122232425
2627282930 

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。