1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go言語におけるgoroutineのdoneチャンネルパターン

Last updated at Posted at 2024-10-19
package main

import (
	"fmt"
	"time"
)

func main() {
	done := make(chan struct{}) // 空の構造体のチャネルを作成

	go func() {
		// 何らかの処理を行う
		time.Sleep(2 * time.Second) // 2秒間スリープ
		fmt.Println("ゴルーチンの処理が完了しました。")
		close(done) // 処理が完了したらdoneチャネルをクローズ
	}()

	// ここで他の処理ができる
	fmt.Println("メインゴルーチンは他の処理を行っています。")

	// doneチャネルがクローズされるのを待つ
	<-done // doneチャネルから受信
	fmt.Println("メインゴルーチンを終了します。")
}
  1. done := make(chan struct{}):

    • 空の構造体の型を持つ done チャネルを作成します。このチャネルは、ゴルーチンが処理を完了したことを通知するために使用されます。
  2. ゴルーチンの起動:

    • go func() で新しいゴルーチンを起動します。この中で処理を行います。
  3. time.Sleep(2 * time.Second):

    • この行で、2秒間の処理を模擬します。実際の処理がここに入ります。
  4. close(done):

    • 処理が完了したら、done チャネルをクローズします。これにより、他のゴルーチン(メインゴルーチンなど)に終了を通知します。
  5. メインゴルーチンの処理:

    • メインゴルーチンでは、fmt.Println("メインゴルーチンは他の処理を行っています。") でメッセージを出力し、他の処理を行います。
  6. <-done:

    • done チャネルからの受信を待ちます。done チャネルがクローズされると、ここでブロックが解除されます。
  7. 終了メッセージ:

    • 最後に、メインゴルーチンが終了することを示すメッセージを出力します。

done チャンネルパターンは、ゴルーチンの終了を管理するための一般的な手法です。以下に、いくつかの典型的な例を示します。

例1: 複数のゴルーチンが同時に処理を実行する

package main

import (
	"fmt"
	"sync"
	"time"
)

func worker(id int, wg *sync.WaitGroup, done chan struct{}) {
	defer wg.Done() // 処理が完了したらワーカーを減らす

	// 何らかの処理を行う
	time.Sleep(time.Duration(id) * 1 * time.Second)
	fmt.Printf("ワーカー %d の処理が完了しました。\n", id)

	// 処理が完了したらdoneチャネルをクローズ
	select {
	case done <- struct{}{}:
	default: // チャネルが既にクローズされた場合は何もしない
	}
}

func main() {
	var wg sync.WaitGroup
	done := make(chan struct{}) // 空の構造体のチャネルを作成

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go worker(i, &wg, done) // 各ゴルーチンを起動
	}

	go func() {
		wg.Wait() // 全てのワーカーが完了するのを待つ
		close(done) // 全てのワーカーが完了したらdoneチャネルをクローズ
	}()

	<-done // doneチャネルから受信
	fmt.Println("全ての処理が完了しました。")
}

説明

  • ワーカー: 各ゴルーチンが処理を行い、処理が完了したら done チャネルに通知します。
  • sync.WaitGroup: 全てのゴルーチンが完了するのを待つために使用します。
  • チャネルのクローズ: 全てのワーカーが完了した後に done チャネルをクローズします。

例2: タイムアウトを利用したゴルーチンの終了

package main

import (
	"fmt"
	"time"
)

func longRunningTask(done chan struct{}) {
	time.Sleep(5 * time.Second)
	fmt.Println("長い処理が完了しました。")
	close(done) // 処理が完了したらdoneチャネルをクローズ
}

func main() {
	done := make(chan struct{})
	go longRunningTask(done)

	select {
	case <-done:
		fmt.Println("処理が完了しました。")
	case <-time.After(2 * time.Second):
		fmt.Println("タイムアウトしました。")
	}
}

説明

  • 長い処理: ゴルーチンで5秒間スリープします。
  • select: done チャネルがクローズされるのを待つか、2秒後にタイムアウトします。
  • タイムアウト: タイムアウトが発生した場合の処理を行います。

例3: 複数のゴルーチンによる計算と結果の収集

package main

import (
	"fmt"
	"sync"
)

func calculate(id int, results chan<- int, done chan struct{}) {
	// 何らかの計算を行う
	result := id * id // 簡単な計算
	results <- result // 結果を送信
	close(done) // 処理が完了したらdoneチャネルをクローズ
}

func main() {
	results := make(chan int)
	done := make(chan struct{})
	var wg sync.WaitGroup

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			calculate(id, results, done)
		}(i)
	}

	go func() {
		wg.Wait()
		close(done) // 全てのゴルーチンが完了したらdoneチャネルをクローズ
	}()

	// 結果を収集
	go func() {
		for result := range results {
			fmt.Println("計算結果:", result)
		}
	}()

	<-done // doneチャネルから受信
	fmt.Println("全ての計算が完了しました。")
}

説明

  • 計算ゴルーチン: 各ゴルーチンが計算を行い、その結果を results チャネルに送信します。
  • 結果の収集: 結果を受信するゴルーチンが results チャネルから結果を出力します。
  • sync.WaitGroup: 全ての計算が完了するのを待ち、done チャネルをクローズします。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?