Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8910

Goメモ-505 (sync.Condのメモ)(Producer-Consumer-Watcher) - いろいろ備忘録日記

$
0
0

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。たまに sync.Cond使うときに、使い方とかよく忘れるので。。。

基本的に一度だけのランデブーポイントを実装する場合はチャネルで事足りるのですが、何回も繰り返しポイントを用意する際には sync.Condが便利です。

以下、サンプルです。

サンプル

main.go

データを生産する役割、データを消費する役割、それらを監視して働いていないときに叩き起こす役割の3つを sync.Condを使って連携させています。

package main

import (
    "flag""log""math/rand/v2""os""os/signal""sync""time"
)

const (
    MAX_ITEM_COUNT = 10
    MAX_PRODUCERS  = 2
    MAX_CONSUMERS  = MAX_ITEM_COUNT / 2
    WATCH_INTERVAL = 1 * time.Second
)

type (
    Args struct {
        debug bool
    }
)

var (
    args Args
)

func init() {
    flag.BoolVar(&args.debug, "debug", false, "debug mode")
}

func main() {
    //// Producer-Consumer-Watcher のサンプル// 各非同期処理のランデブーポイントの制御に// *sync.Cond を利用。//

    log.SetFlags(log.Lmicroseconds)
    log.SetOutput(os.Stdout)

    flag.Parse()

    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    var (
        ch   = make(chanint, MAX_ITEM_COUNT*MAX_PRODUCERS)
        sig  = make(chan os.Signal, 1)
        done = make(chanstruct{})
    )
    deferclose(ch)

    signal.Notify(sig, os.Interrupt)
    gofunc() {
        <-sig
        log.Printf("<<Interrupt>>")
        close(done)
    }()

    var (
        producer = sync.NewCond(&sync.Mutex{})
        consumer = sync.NewCond(&sync.Mutex{})
    )

    // 役割: 生産者と消費者を監視し、必要であれば叩き起こすgo watch(1, ch, producer, consumer)

    // 役割: 生産を行うfor i := range MAX_PRODUCERS {
        go produce(i+1, ch, (i+1)*10000, producer, consumer)
    }

    // 役割: 消費を行うfor i := range MAX_CONSUMERS {
        go consume(i+1, ch, consumer)
    }

    // ゴルーチンの終了待機などについては割愛<-done
    log.Printf("<<DONE>>")

    returnnil
}

func watch(id int, ch <-chanint, producer, consumer *sync.Cond) {
    for {
        func() {
            producer.L.Lock()
            defer producer.L.Unlock()

            iflen(ch) == 0 {
                producer.Broadcast()
                log.Printf("[W][%02d] >>> 0個です。生産しなさい。", id)
            }
        }()

        <-time.After(WATCH_INTERVAL)

        func() {
            consumer.L.Lock()
            defer consumer.L.Unlock()

            iflen(ch) != 0 {
                consumer.Broadcast()
                log.Printf("[W][%02d] >>> 生産されています。消費しなさい。", id)
            }
        }()
    }
}

func produce(id int, ch chan<- int, start int, producer, consumer *sync.Cond) {
    var (
        count int
    )
    for i := start; ; {
        func() {
            producer.L.Lock()
            defer producer.L.Unlock()

            forlen(ch) > cap(ch)/2 {
                dbg("[P][%02d] <<< 消費されるまで待機します。(残:%d)", id, len(ch))
                producer.Wait()
            }
        }()

        func() {
            consumer.L.Lock()
            defer consumer.L.Unlock()

            count = rand.IntN(MAX_ITEM_COUNT)
            for c := range count {
                ch <- i + (c + 1)
            }

            log.Printf("[P][%02d] >>> %d個生産しました。(残:%d)", id, count, len(ch))
            consumer.Broadcast()

            i += count
        }()

        // 次のタスク着手まで少し休憩<-time.After(time.Duration(rand.IntN(500)) * time.Millisecond)
    }
}

func consume(id int, ch <-chanint, consumer *sync.Cond) {
    for {
        func() {
            consumer.L.Lock()
            defer consumer.L.Unlock()

            forlen(ch) == 0 {
                dbg("[C][%02d] <<< 生産されるまで待機します。(残:%d)", id, len(ch))
                consumer.Wait()
            }

            log.Printf("[C][%02d] >>> 消費しました (%v)(残:%d)", id, <-ch, len(ch))
        }()

        // 次のタスク着手まで少し休憩<-time.After(time.Duration(rand.IntN(1000)) * time.Millisecond)
    }
}

func dbg(format string, v ...any) {
    if args.debug {
        log.Printf(format, v...)
    }
}

Taskfile.yml

# https://taskfile.devversion:'3'tasks:default:cmds:- go run . -debug=falseignore_error:true

実行

実行すると、例えば以下のようになります。(実行するたびに結果は異なります。)

$ task
task: [default] go run . -debug=false
09:45:14.397539[P][02]>>>8個生産しました。(残:8)
09:45:14.397744[C][05]>>>消費しました (20001)(残:7)
09:45:14.397803[P][01]>>>7個生産しました。(残:14)
09:45:14.397817[C][02]>>>消費しました (20002)(残:13)
09:45:14.397831[C][01]>>>消費しました (20003)(残:12)
09:45:14.397839[C][03]>>>消費しました (20004)(残:11)
09:45:14.397852[C][04]>>>消費しました (20005)(残:10)
09:45:14.409918[P][02]>>>0個生産しました。(残:10)
09:45:14.456148[P][01]>>>7個生産しました。(残:17)
09:45:14.578567[C][03]>>>消費しました (20006)(残:16)
09:45:14.592732[C][04]>>>消費しました (20007)(残:15)
09:45:14.606827[C][05]>>>消費しました (20008)(残:14)
09:45:14.627954[C][01]>>>消費しました (10001)(残:13)
09:45:14.736242[C][05]>>>消費しました (10002)(残:12)
09:45:14.947805[C][02]>>>消費しました (10003)(残:11)
09:45:15.046041[C][01]>>>消費しました (10004)(残:10)
09:45:15.079229[C][03]>>>消費しました (10005)(残:9)
09:45:15.175388[C][05]>>>消費しました (10006)(残:8)
09:45:15.347710[C][01]>>>消費しました (10007)(残:7)
09:45:15.397873[W][01]>>>生産されています。消費しなさい。
09:45:15.534500[C][04]>>>消費しました (10008)(残:6)
09:45:15.546723[C][04]>>>消費しました (10009)(残:5)
09:45:15.679974[C][05]>>>消費しました (10010)(残:4)
09:45:15.841338[C][02]>>>消費しました (10011)(残:3)
09:45:15.889525[C][03]>>>消費しました (10012)(残:2)
09:45:16.033950[C][04]>>>消費しました (10013)(残:1)
09:45:16.106198[C][05]>>>消費しました (10014)(残:0)
09:45:16.398170[W][01]>>>0個です。生産しなさい。
09:45:16.398205[P][02]>>>3個生産しました。(残:3)
09:45:16.398223[C][03]>>>消費しました (20009)(残:2)
09:45:16.398269[P][01]>>>3個生産しました。(残:5)
09:45:16.398287[C][02]>>>消費しました (20010)(残:4)
09:45:16.398302[C][01]>>>消費しました (20011)(残:3)
09:45:16.398312[C][05]>>>消費しました (10015)(残:2)
09:45:16.422446[C][04]>>>消費しました (10016)(残:1)
09:45:16.422785[C][05]>>>消費しました (10017)(残:0)
09:45:16.570336[P][01]>>>5個生産しました。(残:5)
09:45:16.570385[C][02]>>>消費しました (10018)(残:4)
09:45:16.582587[P][01]>>>4個生産しました。(残:8)
09:45:16.650777[P][02]>>>4個生産しました。(残:12)
09:45:16.713155[C][02]>>>消費しました (10019)(残:11)
09:45:16.805353[C][01]>>>消費しました (10020)(残:10)
09:45:16.836578[P][02]>>>3個生産しました。(残:13)
09:45:16.934218[C][03]>>>消費しました (10021)(残:12)
09:45:17.093537[C][04]>>>消費しました (10022)(残:11)
09:45:17.241811[C][05]>>>消費しました (10023)(残:10)
09:45:17.399157[W][01]>>>生産されています。消費しなさい。
09:45:17.514295[C][03]>>>消費しました (10024)(残:9)
09:45:17.572418[C][01]>>>消費しました (10025)(残:8)
09:45:17.697684[C][02]>>>消費しました (10026)(残:7)
09:45:17.807923[C][05]>>>消費しました (20012)(残:6)
09:45:18.091354[C][04]>>>消費しました (20013)(残:5)
09:45:18.152479[C][01]>>>消費しました (20014)(残:4)
09:45:18.251709[C][03]>>>消費しました (20015)(残:3)
09:45:18.265876[C][01]>>>消費しました (20016)(残:2)
09:45:18.351099[C][02]>>>消費しました (20017)(残:1)
09:45:18.400280[W][01]>>>生産されています。消費しなさい。
09:45:18.497510[C][03]>>>消費しました (20018)(残:0)
09:45:19.400993[W][01]>>>0個です。生産しなさい。
09:45:19.401022[P][02]>>>8個生産しました。(残:8)
09:45:19.401036[P][01]>>>0個生産しました。(残:8)
09:45:19.401060[C][03]>>>消費しました (20019)(残:7)
09:45:19.401071[C][05]>>>消費しました (20020)(残:6)
09:45:19.401094[C][04]>>>消費しました (20021)(残:5)
09:45:19.401108[C][01]>>>消費しました (20022)(残:4)
09:45:19.401121[C][02]>>>消費しました (20023)(残:3)
09:45:19.514361[C][04]>>>消費しました (20024)(残:2)
09:45:19.534457[P][01]>>>7個生産しました。(残:9)
09:45:19.730242[C][01]>>>消費しました (20025)(残:8)
09:45:19.734438[P][02]>>>4個生産しました。(残:12)
09:45:19.891123[C][05]>>>消費しました (20026)(残:11)
09:45:19.974341[C][03]>>>消費しました (10027)(残:10)
09:45:20.165467[C][04]>>>消費しました (10028)(残:9)
09:45:20.196681[C][02]>>>消費しました (10029)(残:8)
09:45:20.386885[C][01]>>>消費しました (10030)(残:7)
09:45:20.402459[W][01]>>>生産されています。消費しなさい。
09:45:20.650229[C][05]>>>消費しました (10031)(残:6)
09:45:20.686436[C][01]>>>消費しました (10032)(残:5)
09:45:20.735690[C][03]>>>消費しました (10033)(残:4)
09:45:20.851986[C][01]>>>消費しました (20027)(残:3)
09:45:20.864154[C][02]>>>消費しました (20028)(残:2)
09:45:21.088516[C][03]>>>消費しました (20029)(残:1)
09:45:21.144719[C][04]>>>消費しました (20030)(残:0)
09:45:21.403257[W][01]>>>0個です。生産しなさい。
09:45:21.403294[P][02]>>>1個生産しました。(残:1)
09:45:21.403311[C][02]>>>消費しました (20031)(残:0)
09:45:21.403326[P][01]>>>5個生産しました。(残:5)
09:45:21.513590[P][01]>>>0個生産しました。(残:5)
09:45:21.524788[C][01]>>>消費しました (10034)(残:4)
09:45:21.526249[C][01]>>>消費しました (10035)(残:3)
09:45:21.564602[C][05]>>>消費しました (10036)(残:2)
09:45:21.644747[P][01]>>>1個生産しました。(残:3)
09:45:21.668940[P][02]>>>4個生産しました。(残:7)
09:45:21.773198[P][01]>>>9個生産しました。(残:16)
09:45:21.829339[C][01]>>>消費しました (10037)(残:15)
09:45:22.034972[C][03]>>>消費しました (10038)(残:14)
09:45:22.086167[C][04]>>>消費しました (10039)(残:13)
09:45:22.147829[C][02]>>>消費しました (20032)(残:12)
09:45:22.296113[C][01]>>>消費しました (20033)(残:11)
09:45:22.312208[C][04]>>>消費しました (20034)(残:10)
09:45:22.350416[C][05]>>>消費しました (20035)(残:9)
09:45:22.403627[W][01]>>>生産されています。消費しなさい。
09:45:22.538209[C][05]>>>消費しました (10040)(残:8)
09:45:22.910731[C][04]>>>消費しました (10041)(残:7)
09:45:22.922483[C][03]>>>消費しました (10042)(残:6)
09:45:23.008894[C][05]>>>消費しました (10043)(残:5)
09:45:23.030455[C][02]>>>消費しました (10044)(残:4)
09:45:23.106724[C][01]>>>消費しました (10045)(残:3)
09:45:23.138976[C][01]>>>消費しました (10046)(残:2)
09:45:23.237293[C][05]>>>消費しました (10047)(残:1)
09:45:23.249501[C][03]>>>消費しました (10048)(残:0)
09:45:23.404371[W][01]>>>0個です。生産しなさい。
09:45:23.404408[P][01]>>>6個生産しました。(残:6)
09:45:23.404429[C][02]>>>消費しました (10049)(残:5)
09:45:23.404454[C][03]>>>消費しました (10050)(残:4)
09:45:23.404464[C][04]>>>消費しました (10051)(残:3)
09:45:23.404476[C][05]>>>消費しました (10052)(残:2)
09:45:23.404482[P][02]>>>8個生産しました。(残:10)
09:45:23.743011[P][01]>>>1個生産しました。(残:11)
09:45:23.767384[C][02]>>>消費しました (10053)(残:10)
09:45:23.813520[C][01]>>>消費しました (10054)(残:9)
09:45:23.941829[C][05]>>>消費しました (20036)(残:8)
09:45:23.949129[P][01]>>>8個生産しました。(残:16)
09:45:24.045360[C][05]>>>消費しました (20037)(残:15)
09:45:24.176581[C][01]>>>消費しました (20038)(残:14)
09:45:24.178758[C][03]>>>消費しました (20039)(残:13)
09:45:24.282132[C][04]>>>消費しました (20040)(残:12)
09:45:24.405397[W][01]>>>生産されています。消費しなさい。
09:45:24.544682[C][05]>>>消費しました (20041)(残:11)
^C09:45:24.655624<<Interrupt>>09:45:24.655692 <<DONE>>

参考情報

pkg.go.dev

mattn.kaoriya.net

lestrrat.medium.com

tech.yappli.io

victoriametrics.com

Goのおすすめ書籍


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

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


Viewing all articles
Browse latest Browse all 8910

Latest Images

Trending Articles