草庐IT

go - golang的递归并发

coder 2023-07-01 原文

我想在一些 goroutine 之间分配一些负载。如果事先知道任务的数量,那么就很容易组织起来。例如,我可以用 WaitGroup 扇出。

nTasks := 100
nGoroutines := 10

// it is important that this channel is not buffered
ch := make(chan *Task)
done := make(chan bool)
var w sync.WaitGroup
// Feed the channel until done
go func () {
    for i:= 0; i < nTasks; i++ {
        task := getTaskI(i)
        ch <- task
    }
    // as ch is not buffered once everything is read we know we have delivered all of them
    for i:=0; i < nGoroutines; i++ {
        done <- false
    }
}()
for i:= 0; i < nGoroutines; i ++ {
    w.Add(1)
    go func () {
        defer w.Done()
        select {
        case task := <-ch:
            doSomethingWithTask(task)
        case <- done:
            return
        }
    }()
}
w.Wait()
// All tasks done, all goroutines closed

但是,在我的例子中,每个任务都会返回更多待完成的任务。举例来说,我们从抓取的网络中接收所有链接的爬虫。我最初的预感是有一个主循环,我可以在其中跟踪已完成任务和待处理任务的数量。完成后,我向所有 goroutines 发送完成信号:

nGoroutines := 10
ch := make(chan *Task, nGoroutines)
feedBackChannel := make(chan * Task, nGoroutines)
done := make(chan bool)

for i:= 0; i < nGoroutines; i ++ {
    go func () {
        select {
        case task := <-ch:
            task.NextTasks = doSomethingWithTask(task)
            feedBackChannel <- task
        case <- done:
            return
        }
    }()
}

// seed first task
ch <- firstTask
nTasksRemaining := 1

for nTasksRemaining > 0 {
    task := <- feedBackChannel
    nTasksRemaining -= 1
    for _, t := range(task.NextTasks) {
        ch <- t
        nTasksRemaining++
    }
}
for i:=0; i < nGoroutines; i++ {
    done <- false
}

但是,这会产生死锁。例如,如果 NextTasks 大于 goroutines 的数量,那么主循环将在第一个任务完成时停止。但是第一个任务无法完成,因为反馈被阻塞,因为 mainLoop 正在等待写入。

解决此问题的一个“简单”方法是异步发布到 channel : 而不是做 feedBackChannel <- taskgo func () {feedBackChannel <- task}() .现在,这感觉像是一个可怕的黑客攻击。特别是因为可能有数十万个任务。

避免这种僵局的好方法是什么?我搜索过并发模式,但大多数都是更简单的事情,例如扇出或管道,其中后期阶段不会影响早期步骤。

最佳答案

如果我正确理解您的问题,您的解决方案将非常复杂。这里有几点。希望对您有所帮助。

  • 正如人们在评论中提到的那样,启动一个 goroutine 很便宜(内存和它们之间的切换都比操作系统级别的读取便宜得多)而且你可以拥有数十万个。让我们假设出于某些原因您希望拥有 worker goroutine。
  • 您可以关闭 ch 而不是 done channel channel 而不是select你只是range通过您的 channel 获取任务。
  • 我看不出分开的意义chfeedBackChannel只需将您的每项任务插入 ch并增加其容量。
  • 如前所述,当您尝试将新任务加入队列时可能会遇到死锁。我的解决方案非常天真。只需增加其容量,直到您确定它不会溢出(如果 cap(ch) - len(ch) < threshold ,您也可以记录警告)。如果您创建一个容量为 100 万的 channel (指针),大约需要 8 * 1e6 ~= 8MB的公羊。

关于go - golang的递归并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47693338/

有关go - golang的递归并发的更多相关文章

  1. ruby-on-rails - 获取并发布相同匹配项的请求 - 2

    在我的路线文件中我有:match'graphs/(:id(/:action))'=>'graphs#(:action)'如果是GET请求(工作)或POST请求(不工作),我想匹配它我知道我可以使用以下方法在资源中声明POST请求:post'/'=>:show,:on=>:member但是我怎样才能为比赛做到这一点呢?谢谢。 最佳答案 如果你同时想要POST和GETmatch'graphs/(:id(/:action))'=>'graphs#(:action)',:via=>[:get,:post]编辑默认值可以设置如下match'g

  2. ruby - 递归地将所有数字字符串转换为 Ruby 哈希中的整数 - 2

    我有一个随机大小的散列,它可能有类似"100"的值,我想将其转换为整数。我知道我可以使用value.to_iifvalue.to_i.to_s==value来做到这一点,但我不确定我将如何在我的散列中递归地做到这一点,考虑到一个值可以是一个字符串,或一个数组(哈希或字符串),或另一个哈希。 最佳答案 这是一个非常简单的递归实现(尽管必须同时处理数组和散列会增加一些技巧)。deffixnumifyobjifobj.respond_to?:to_i#IfwecancastittoaFixnum,doit.obj.to_ielsifobj

  3. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

  4. ruby - 为什么我用递归得到 "stack level too deep"? - 2

    我有这个ruby代码:defget_sumnreturn0ifn似乎正在为999之前的值工作。当我尝试9999时,它给了我这个:stackleveltoodeep(SystemStackError)所以,我添加了这个:RubyVM::InstructionSequence.compile_option={:tailcall_optimization=>true,:trace_instruction=>false}但什么也没发生。我的ruby版本是:ruby1.9.3p392(2013-02-22revision39386)[x86_64-darwin12.2.1]我还增加了机器的堆栈大

  5. ruby - 构建网络蜘蛛时,应该使用递归吗? - 2

    构建一个深度优先的网络蜘蛛,这意味着它将访问第一页上的所有链接,然后转到每个链接,并访问所有第二页上的链接...你应该使用递归吗?我发现这是CPU密集型的。defrecursion()linkz_on_first_page.eachdo|link|recursion(link)endendrecursion(firstpage) 最佳答案 绝对不是,由于万维网的实际性质,您很快就会遇到问题。当您访问带有主导航部分的网站时,每个页面都链接到其他页面,您就进入了一个无限循环。您可以跟踪您处理了哪些链接,但即便如此,递归循环并不真正适合万

  6. ruby-on-rails - 如何以递归方式将 YAML 文件扁平化为 JSON 对象,其中键是点分隔的字符串? - 2

    例如,如果我有YAML文件en:questions:new:'NewQuestion'other:recent:'Recent'old:'Old'这最终会变成一个json对象,例如{'questions.new':'NewQuestion','questions.other.recent':'Recent','questions.other.old':'Old'} 最佳答案 由于问题是关于在Rails应用程序上使用YAML文件进行i18n,因此值得注意i18ngem提供了一个辅助模块I18n::Backend::Flatten完全像

  7. ruby-on-rails - Textmate 'Go to symbol' 相当于 Vim - 2

    在Railcasts上,我注意到一个非常有趣的功能“转到符号”窗口。它像Command-T一样工作,但显示当前文件中可用的类和方法。如何在vim中获取它? 最佳答案 尝试:helptags有各种程序和脚本可以生成标记文件。此外,标记文件格式非常简单,因此很容易将sed(1)或类似的脚本组合在一起,无论您使用何种语言,它们都可以生成标记文件。轻松获取标记文件(除了下载生成器之外)的关键在于格式化样式而不是实际解析语法。 关于ruby-on-rails-Textmate'Gotosymbol

  8. ruby - 为什么尾递归 gcd 比 rubinius 的 while 循环更快 - 2

    我有这两个gcd函数的实现:defgcd1(a,b)ifa==baelsifa>bif(a%b)==0belsegcd1(a%b,b)endelseif(b%a)==0aelsegcd1(a,b%a)endendenddefgcd2(a,b)if(a==b)returnaelsifb>amin,max=a,belsemin,max=b,aendwhile(max%min)!=0min,max=max%min,minendminend函数gcd1是尾递归的,而gcd2使用while循环。我已经验证rubinius通过对阶乘函数进行基准测试来执行TCO,只有阶乘函数基准测试显示递归版本和迭

  9. ruby-on-rails - 为什么我的 helper 递归方法不返回每个值? - 2

    我想显示一个由gem祖先管理的类别树。我想使用一个助手,它会递归地遍历树并一个一个地返回类别,暂时没有html标签或内容。moduleCategoriesHelperdefdisplay_tree(category)ifcategory.has_children?category.children.eachdo|sub_category|display_tree(sub_category)puts(sub_category.name)#tocheckifitgoeshereendendcategory.nameendendcategory参数是根类别之一。它应该返回什么?在网页中:它仅

  10. Ruby 并发/异步处理(简单用例) - 2

    我一直在研究ruby​​的并行/异步处理能力,并阅读了许多文章和博客文章。我查看了EventMachine、Fibers、Revactor、Reia等。不幸的是,我无法为这个非常简单的用例找到简单、有效(且非IO阻塞)的解决方案:File.open('somelogfile.txt')do|file|whileline=file.gets#(R)ReadfromIOline=process_line(line)#(P)Processthelinewrite_to_db(line)#(W)WritetheoutputtosomeIO(DBorfile)endend你看到了吗,我的小脚本正

随机推荐