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


関連記事

laravel logo

Laravelのブラウザテスト「Dusk」で非同期で重たい処理のテストを実装してみよう

1 はじめに2 JavaScriptの式で待機する2.1 テスト対象となるコード2.2 Duskのテストコード3 DOM要素の表示を待つ3.1 テスト対象となるコード3.2 Duskテストコードの実装 ...

GraphQL

いまさら学ぶGraphQL〜概要編〜

1 はじめに2 GraphQLとは?2.1 なぜGraphQLは誕生した?2.1.1 過剰な取得2.1.2 過少な取得2.1.3 エンドポイントの管理3 GraphQLのメリットとデメリット3.1 G ...

PHPerだけどKotlinを勉強したって良いよね その2〜コンストラクタ編〜

1 はじめに2 コンストラクタ2.1 プライマリコンストラクタ2.2 セカンダリコンストラクタ2.3 ニックネームのみを入力した人2.4 ニックネームと email を入力した人2.5 Faceboo ...

Stripe Connectを使って複合プランの継続課金を実装

1 はじめに1.1 想定する継続課金1.2 想定するシチュエーション2 継続課金商品の作成2.1 プランの継続課金商品の作成2.2 ID数の継続課金商品の作成3 実装3.1 プランAとID数を30契約 ...

rails

configに追記したのに、rails runnerが起動しない?

1 はじめに1.1 環境2 rails runnerを使用してみる2.1 バッチファイルの作成2.2 configにパスを追記2.3 実行3 ところが…3.1 解決方法4 おまけ(runnerコマンド ...

フォロー

blog-page_side_responsive

2020年4月
 1234
567891011
12131415161718
19202122232425
2627282930  

アプリ情報

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