半异步代码逻辑

时间:2017-03-11 02:04:30

标签: multithreading asynchronous go goroutine

我正在努力找出一种将同步流与异步行为混合在一起的工作设计。

我有4个组成部分:

  1. 播种机
  2. 工人
  3. 出版商
  4. 更新
  5. 我唯一的限制是,一旦Seeder播种数据,它必须被阻止,直到Updater没有完全处理完所有任务。前三个组件可以很容易地同步,但更新程序必须并行工作,否则将永远完成任务。

    所以流程是:

    Seeder -> Worker -> Publisher -> Updater --> Seeder -> Worker -> Publisher -> Updater ...

    此流程必须永远旋转。

    播种和更新是针对数据库的。不幸的是,这个特定的数据库并不允许不同的设计。

    我所做的最好的事情是使用sync.WaitGroup同步Updater goroutines并将其他所有内容保持为同步状态。更新程序的数据是通过渠道提供的。

    这是一个简化的代码(没有错误,没有多少逻辑)。

    func main() {
        var wg sync.WaitGroup
        c := make(chan Result, 100)
    
        for {
            data := Seeder()
            msgs := Worker(data)
            results := Publisher(msgs)
    
            for i := 0; i < 10; i++ {
                wg.Add(1)
                go func(){
                    defer wg.Done()
                    data := <- c
    
                    // this is the updater
                }(&wg)
            }
    
            for _, result := range results {
                c <- result
            }
            wg.Wait()
        }
    }
    

    结果是代码一直运行直到它在某个周期停止并且永远不会向前移动。我玩了很多变量,加载100行而不是10k,结果差别不大。

    我还尝试传递一个包含频道的结构并以异步方式运行所有内容,但我更难以确定Updater何时完成,因此我可以解锁播种器。

    任何指针都表示赞赏。

1 个答案:

答案 0 :(得分:0)

很难分辨,因为您的代码无法编译和运行,并且您不清楚如何使用c。至少有一件事是肯定的:wg应该通过引用传递,而不是通过值传递(sync.WaitGroup具有nocopy注释)。然后,我假设您使用c将值发送到更新程序。但是你没有提供他们的代码,所以我只能猜测。例如,假设调度发生时,前9个goroutines将全部读取信道;然后,最后一个例程永远被阻止,永远不会释放WaitGroup。在这种情况下,一个简单的解决方案是在调用wg.Wait()之前,在最外层for循环的每次迭代中创建一个新通道(将行3向下移动两行)和close c。您的更新程序必须能够处理read from a close channel

[编辑]我认为你要找的是这样的:

package main

import (
    "fmt"
    "sync"
)

// Result is a type
type Result struct {
    I int
}

// Seeder is a function
func Seeder() []int {
    fmt.Println("Seeding")
    return []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
}

// Worker is a function
func Worker(data []int) []int {
    return data
}

// Publisher is a function
func Publisher(data []int) []Result {
    var r []Result
    for i := 0; i < len(data); i++ {
        r = append(r, Result{I: data[i]})
    }
    return r
}

func updater(c chan Result, wg *sync.WaitGroup) {
    for _ = range c {
        // update here
        wg.Done()
    }
}

func main() {
    var wg sync.WaitGroup

    c := make(chan Result, 100)
    for i := 0; i < 10; i++ {
        go updater(c, &wg)
    }

    for {
        data := Seeder()
        msgs := Worker(data)
        results := Publisher(msgs)

        wg.Add(len(results))
        for _, result := range results {
            c <- result
        }
        wg.Wait()
    }
}