当前位置: 首页 > 面试题库 >

使用通道同步多个goroutine

袁晋鹏
2023-03-14
问题内容

我需要使用单个任务队列和单个结果队列来启动许多工作程序。每个工人都应该以不同的goroutine开始。我需要等到所有工作人员都将完成并且任务队列将为空后再退出程序。我已经准备了goroutine同步的小例子。主要思想是我们将排队的任务计数,并等待所有工人完成工作。但是当前的实现有时会遗漏值。为什么会发生这种情况以及如何解决问题?示例代码:

import (
    "fmt"
    "os"
    "os/signal"
    "strconv"
)

const num_workers = 5

type workerChannel chan uint64

// Make channel for tasks
var workCh workerChannel
// Make channel for task counter
var cntChannel chan int

// Task counter
var tskCnt int64

// Worker function
func InitWorker(input workerChannel, result chan string, num int) {
    for {
        select {
        case inp := <-input:
            getTask()
            result <- ("Worker " + strconv.Itoa(num) + ":" + strconv.FormatUint(inp, 10))
        }
    }
}

// Function to manage task counter
// should be in uniq goroutine
func taskCounter(inp chan int) {
    for {
        val := <-inp
        tskCnt += int64(val)
    }
}

// Put pask to the queue
func putTask(val uint64) {
    func() {
        fmt.Println("Put ", val)
        cntChannel <- int(1)
        workCh <- val
    }()
}

// Get task from queue
func getTask() {
    func() {
        cntChannel <- int(-1)
    }()
}

func main() {
// Init service channels
    abort := make(chan os.Signal)
    done := make(chan bool)

// init queue for results
    result := make(chan string)

// init task queue
    workCh = make(workerChannel)

// start some workers
    for i := uint(0); i < num_workers; i++ {
        go InitWorker(workCh, result, int(i))
    }

// init counter for synchro
    cntChannel = make(chan int)
    go taskCounter(cntChannel)

// goroutine that put some tasks into queue
    go func() {
        for i := uint(0); i < 21; i++ {
            putTask(uint64(i))
        }

        // wait for processing all tasks and close application
        for len(cntChannel) != 0 {}
        for tskCnt != 0 {}
        for len(workCh) != 0 {}
        for len(result) != 0 {}

        // send signal for close
        done <- true
    }()

    signal.Notify(abort, os.Interrupt)
    for {
        select {
        case <-abort:
            fmt.Println("Aborted.")
            os.Exit(0)

        // print results
        case res := <-result:
            fmt.Println(res)

        case <-done:
            fmt.Println("Done")
            os.Exit(0)
        }
    }
}

问题答案:

使用sync.WaitGroup等待goroutine完成。关闭通道以使通道上的循环读取退出。

package main

import (
    "fmt"
    "sync"
)

type workerChannel chan uint64

const num_workers = 5

func main() {

    results := make(chan string)
    workCh := make(workerChannel)

    // Start workers
    var wg sync.WaitGroup
    wg.Add(num_workers)
    for i := 0; i < num_workers; i++ {
        go func(num int) {
            defer wg.Done()
            // Loop processing work until workCh is closed
            for w := range workCh {
                results <- fmt.Sprintf("worker %d, task %d", num, w)
            }

        }(i)
    }

    // Close result channel when workers are done
    go func() {
        wg.Wait()
        close(results)
    }()

    // Send work to be done
    go func() {
        for i := 0; i < 21; i++ {
            workCh <- uint64(i)
        }
        // Closing the channel causes workers to break out of loop
        close(workCh)
    }()

    // Process results. Loop exits when result channel is closed.
    for r := range results {
        fmt.Println(r)
    }
}

https://play.golang.org/p/ZifpzsP6fNv



 类似资料:
  • 我有多个线程,它们都从一个对象运行。我希望“主线程”单独运行,直到某一点,然后它等待,所有其他线程一起运行,然后主线程唤醒,等等。。。。。我无法同步线程。我要么得到一个非法的监视器状态异常,要么它卡在一个“等待”循环中,该循环应该接收一个永远不会到达的“通知”。 更具体地说,我有一个带有数组的对象。阵列中的每个单元都有一个线程,该线程检查相邻单元,然后使用该信息更改其单元。为了有序地进行更改,我希

  • 我们使用通道来同步协程之间的执行。 下面的例子是通过获取同步通道数据来阻塞程序执行的方法来等待另一个协程运行结束的。 也就是说main函数所在的协程在运行到<-done语句的时候将一直等待worker函数所在的协程执行完成,向通道写入数据才会(从通道获得数据)继续执行。 package main import "fmt" import "time" // 这个worker函数将以协程的方式运行 /

  • 我需要实现一个由多个步骤组成的集成流程,每个步骤都可以由不同数量的处理器(插件)执行。 到目前为止我所拥有的: 预期的行为如下: 通过网关发送第一个请求 一切正常,但结果不是预期的,我只收到2个(随机)项目,而不是4个。 我认为问题在于聚合器仅在两个项目之后触发发布,因为“step/2”通道中的“apply sequence”覆盖了“step/1”中的“apply sequence”。所以问题是:

  • 我是新来的,想知道一些很基本的问题,我不能弄清楚。 为了发挥作用(对实际需要的抽象),我需要: 用常数固定的元素初始化字符串片断 遍历此切片并为每个元素运行一个goroutine 每个goroutine将花费一定的时间来处理元素(随机秒持续时间) 作业完成后,我希望goroutine将结果推送到一个通道 然后我需要捕获来自这个通道的所有结果(在一个从主goroutine调用的函数中),将它们附加到

  • 问题内容: 我是Golang的新手。现在,我正在尝试找出如何在Golang中建立任意一对一频道的方法,其设置如下: 说我有两个goroutines numgen1和numgen2同时执行并将数字写入通道num1resp。num2。我想在新进程addnum中添加从numgen1和numgen2发送的数字。我已经尝试过这样的事情: 但这似乎令人遗憾。有人可以给我一些想法吗? 非常感谢您的帮助。 问题答

  • 多个同步服务器 Since you have full control of express instance lifecycle, it's not a problem to create a few multiple simultaneous servers (e.g. both HTTP & HTTPS). Example: 因为你已经可以完全控制express实例的生命周期了,所以创建多个