Open4

Goスレッドプログラミング入門

unokununokun

前提条件

  • スレッドとは何かを知っている
  • スレッドプログラミングある程度経験済み
unokununokun

スレッド実行

java

スレッド処理実行クラスを使う。
スレッドで実行する処理はそれぞれ実装する。

Threadクラスを継承したクラスを作成して実行する。
Web開発のためのJava入門 10章:スレッド : 富士通アプリケーションズ

class ProcA implements Runnable {
  public void methodA() {
  }
  public void run() {
    // 実行したい処理
    methodA()
  }
}
class ProcB implements Runnable {
  public void methodB() {
  }
  public void run() {
    // 実行したい処理
    methodB()
  }
}
// 実行
Thread threadA = new Thread(new ProcA());
threadA.start(); 
Thread threadB = new Thread(new ProcB());
threadB.start(); 

Go

キーワードgoを使って関数呼び出しするとスレッドが作成される。
Go言語の場合、スレッド(クラスに相当するモノ)をGoroutineと呼ぶ。

プログラム処理(main)もGoroutine上で動作する。メインGoroutineと呼ぶ。
これはJavaも同じ。

func funcA() {
}
func main() {
  go funcA();
}
unokununokun

スレッド間のメソッド呼び出し

Java

クラスのメソッドは何の制限もなく呼び出すことができる。
ただし、複数スレッドから非同期にリソース更新させたくない場合にはsynchronizedキーワードを追加する。
これにより単一スレッドからのアクセスに制限できる。

  public synchronized void methodB() {
  }

##Go
メソッド呼び出しはchannelを使って行う。
goのchannelは、スレッド機能(goroutine)と送受信データ管理機能を持つ。
これにより、スレッド(goroutine)間のメッセージを送受信(Message Passing)を実現している。

以下の例は、メインgoroutineから別goriutine(新しいchannelを実行する)にメッセージ送信している。
Go by Example: Channels

func main() {

	// Create a new channel with `make(chan val-type)`.
	// Channels are typed by the values they convey.
	messages := make(chan string)

	// _Send_ a value into a channel using the `channel <-`
	// syntax. Here we send `"ping"`  to the `messages`
	// channel we made above, from a new goroutine.
	go func() { messages <- "ping" }()

	// The `<-channel` syntax _receives_ a value from the
	// channel. Here we'll receive the `"ping"` message
	// we sent above and print it out.
	msg := <-messages
	fmt.Println(msg)
}
unokununokun

channelについて

作成

受信するデータの型を指定して作成する。

messages := make(chan string)

送信

<-キーワードを使ってデータを送信する。
送信元スレッド(処理を実行しているgoroutine)
送信先スレッド(channelが動作しているgoroutine)

messages <- "ping"

受信

<-キーワードを使ってデータを受信する
受信元スレッド(channelが動作しているgoroutine)
受信先スレッド(処理を実行しているgoroutine)

msg := <-messages

##buffering(複数データ受信)
デフォルトではchannelは単一データのみを受信(保持)する。複数データ受信したい場合には、channel作成関数に引数(個数)を追加する。

文字列型のデータを2つ保持するchannelを作成する。
messages := make(chan string, 2)

messages := make(chan string, 2)

##Channel Synchronization
単一データ受信channelからデータを受信する場合、データ受信するまで受信先スレッドがロックされる。
データ受信していない場合、プログラム(メインgoroutine)の終了とともにすべてのgoroutineが終了する。
Channel Synchronization

func worker(done chan bool) {
         //...
	// Send a value to notify that we're done.
	done <- true
}

func main() {
	// Start a worker goroutine, giving it the channel to notify on.
	done := make(chan bool, 1)
	go worker(done)

	// Block until we receive a notification from the worker on the channel.
	<-done
}

複数goroutineの終了を待つ場合には、WaitGroupsを使うことができる。
Go by Example: WaitGroups