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


関連記事

docker-syncでファイルの同期を高速化する

1 はじめに2 docker-syncの導入3 設定ファイルの作成3.1 docker-composer.yml3.2 docker-compose-dev.yml3.3 docker-sync.ym ...

js

GoogleAppsScriptを使ってmBaaSの定期実行処理を実装する

1 はじめに1.1 簡単な状況説明1.2 定期実行を行う方法2 実装2.1 実装の流れ2.2 JavaScriptの実装2.3 スクリプトをアップロードする2.4 Google Apps Script ...

WebアプリからLINEのメッセージを送る方法

1 はじめに2 Messaging APIとは2.1 Messaging APIの仕組み2.2 Webhookイベント2.3 メッセージオブジェクトの種類2.4 料金形態3 LINE Develope ...

Stripe Connectを使って継続課金を実装

1 はじめに2 商品・価格の登録2.1 マイグレーション2.2 製品・価格登録処理の実装2.3 Stripe管理画面での確認3 サブスクリプション登録3.1 事前準備3.2 課金処理の実装3.3 St ...

laravel logo

Laravel Cashier サブスクリプションに使用するテーブルを理解する

1 はじめに2 Laravel Cashierのテーブル2.1 usersテーブル2.2 subscriptionsテーブル2.3 supscription_itemsテーブル3 課金情報の更新方法4 ...

フォロー

blog-page_side_responsive

2020年4月
 1234
567891011
12131415161718
19202122232425
2627282930  

アプリ情報

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