37365


以前に[GO言語での並列計算の仕組みについて](https://ja.stackoverflow.com/questions/36946/go%E8%A8%80%E8%AA%9E%E3%81%A7%E3%81%AE%E4%B8%A6%E5%88%97%E8%A8%88%E7%AE%97%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)について質問をさせていただきました。  
今回はそれをもう少し発展させたいのですが、スタックしてしまい、また皆様のお知恵を拝借したく、ここで質問させていただいています。

まずtoy scriptを示した後に、質問させていただきます。

[code]

    package main
    
    import (
          "fmt"
          "math/rand"
          "time"
          "sync"
          "runtime"
    )
    
    var wg sync.WaitGroup
    
    func Sleep() {
        defer wg.Done()
    
        r := rand.Intn(3);
        if r == 0 {
          time.Sleep(10 * time.Millisecond) //0.01秒停止
        } else if r == 1 {
          time.Sleep(100 * time.Millisecond)  //0.1秒停止
        } else if r == 2 {
          time.Sleep(3000 * time.Millisecond) //3秒停止
        }
    }
    
    func main() {
      N := 8;
      runtime.GOMAXPROCS(N)
    
      idx := 0;
      for i:=0; i<18; i++ {
        fmt.Println("process:", i)
        wg.Add(1)
        idx += 1;
        go Sleep();
        if idx%N == 0 {
            wg.Wait();
        }
      }
    
      wg.Wait();
    }
    
[/code]

このscriptでは、goroutineを用いまして、Sleep functionを動かしています。

このfunctionでは、ランダムの数字により、Sleepの時間を変えるようにしています。  
８個並列して計算させますと、そのうちの一個以上で「3秒停止」になるように設定しています。  
この場合、「wg.Wait()」があるため、他の計算が終了していても、8つ中一つでも「3秒停止」があれば、その回では「3秒間停止」する必要が出てきてしまいます。

質問ですが、goroutineでは、終わったものから次の計算をさせるような機能はあるのでしょうか？

具体的には、以下のようなことを想定しております。  
8つ中1つだけ「3秒停止」であり、他７つは「0.1秒停止」の場合、  
0.1秒停止が終わった7つのプロセスだけ進むようなことは可能でしょうか？  
ただし、最大８プロセスだけ動かすことを仮定しております。  
（実際には、それ以上動かしますと、プロセス間で干渉が起こり、計算時間が長くなってしまいますので。）

ご教授、よろしくお願いします。


CC BY-SA 3.0
2017-08-20T21:02:40.850
37361
2017-08-21T03:20:33.657
2017-08-20T21:11:31.690
5044
11048
question
go
GO言語での並列計算の仕組みについて (パート２)


各 goroutine 間に依存関係が無く、ただ並列に動いている goroutine の最大数を 8
にしたいだけなのであれば、ワーカースレッドの形にすると分かりやすいのではないかと思いました。

[code]

    package main
    
    import (
        "log"
        "math/rand"
        "runtime"
        "sync"
        "time"
    )
    
    func Sleep(id int, wg *sync.WaitGroup, req chan int) {
        log.Printf("ID %d: invoked!\n", id)
        defer log.Printf("ID %d: finished!\n", id)
        defer wg.Done()
    
        for {
            r, ok := <-req
            if !ok {
                return
            }
    
            log.Printf("ID %d: waiting... (r = %d)", id, r)
            if r == 0 {
                time.Sleep(10 * time.Millisecond) //0.01秒停止
            } else if r == 1 {
                time.Sleep(100 * time.Millisecond) //0.1秒停止
            } else if r == 2 {
                time.Sleep(3000 * time.Millisecond) //3秒停止
            }
        }
    }
    
    func main() {
        N := 8
        runtime.GOMAXPROCS(N)
        req := make(chan int)
    
        var wg sync.WaitGroup
    
        // ワーカーを N 個作る
        for i := 0; i < N; i++ {
            wg.Add(1)
            go Sleep(i, &wg, req)
        }
    
        // 仕事を作る
        for i := 0; i < 18; i++ {
            req <- rand.Intn(3)
        }
        close(req)
    
        wg.Wait()
    }
    
[/code]

([Go Playground](https://play.golang.org/p/wkxeiFoLZn))

チャネルを close すると `ok` が `false` になることや、チャネルの送受信まわりでブロッキングが起こっていることに注意してください。


CC BY-SA 3.0
2017-08-21T03:20:33.657
37365
2017-08-21T03:20:33.657
19110
37361
answer