いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-272 (ゴルーチンリークが発生するパターン (2))(送信側、受信側がいなくなってしまう)

概要

以下、自分用のメモです。

ゴルーチン(goroutine) って、とても便利ですが、たまにリークさせてしまうことがあったりします。

基本的なパターンは決まっているのですが、見つけるのも難しいので可能な限り、そうならないように注意した方が良いですね。

送信しようとしているが受信側がいなくなってしまうパターン

package leak

import (
    "context"
    "time"

    "github.com/devlights/gomy/output"
)

// AbandonedSender -- goroutineリークが発生するパターンのサンプルです。
//
// チャネルの送受信の実装があるが、タイミングによっては受信側がいなくなってしまうパターン。
// 送信側のgoroutineが永遠に待ち続けるので終了しません。
//
// 解決方法としては、Bufferedなチャネルを使うこと。
//
// REFERENCES:
//   - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
func AbandonedSender() error {
    var (
        ctx, cxl = context.WithTimeout(context.Background(), 10*time.Millisecond)
        ch       = make(chan int)
        iowait   = func() {
            time.Sleep(1 * time.Second)
        }
        fn = func(ch chan<- int) {
            iowait()
            ch <- 1
            output.Stdoutl("[send]", 1)
        }
    )
    defer cxl()

    go fn(ch)

    select {
    case v := <-ch:
        output.Stdoutl("[recv]", v)
    case <-ctx.Done():
    }

    //
    // チャネルからデータを受信するものがいなくなるので
    // 上のgoroutineはプロセスが起動中は永遠に終了しません。
    //
    time.Sleep(1 * time.Second)

    return nil
}

受信しようとしているが送信側がいなくなってしまうパターン

package leak

import (
    "context"
    "time"

    "github.com/devlights/gomy/output"
)

// AbandonedReceiver -- goroutineリークが発生するパターンのサンプルです。
//
// チャネルの送受信の実装があるが、タイミングによっては送信側がいなくなってしまうパターン。
// 受信側のgoroutineが永遠に待ち続けるので終了しません。
//
// 解決方法としては、送信側が適切に使い終わったチャネルを閉じること。
//
// REFERENCES:
//   - https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
func AbandonedReceiver() error {
    var (
        ctx, cxl = context.WithTimeout(context.Background(), 10*time.Millisecond)
        ch       = make(chan int)
        iowait   = func() {
            time.Sleep(1 * time.Second)
        }
        fn = func(ch <-chan int) {
            iowait()
            data := <-ch
            output.Stdoutl("[recv]", data)
        }
    )
    defer cxl()

    go fn(ch)

    select {
    case ch <- 1:
        output.Stdoutl("[send]", 1)
    case <-ctx.Done():
    }

    //
    // チャネルにデータを送信するものがいなくなるので
    // 上のgoroutineはプロセスが起動中は永遠に終了しません。
    //
    time.Sleep(1 * time.Second)

    return nil
}

参考情報

https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee

try-golang/examples/basic/goroutines/leak at master · devlights/try-golang · GitHub

Go言語による並行処理

Go言語による並行処理

Amazon


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。