Concurrency Go-lang关闭管道死锁

Concurrency Go-lang关闭管道死锁,concurrency,go,deadlock,pipeline,Concurrency,Go,Deadlock,Pipeline,我正在使用Go语言进行数据导入工作,我希望将每个步骤编写为闭包,并使用通道进行通信,也就是说,每个步骤都是并发的。问题可以通过以下结构来定义 从数据源获取小部件 将源1的翻译添加到小部件 将源代码2的翻译添加到小部件 将定价从源1添加到小部件 将WidgetRevisions添加到小部件。 将源1的翻译添加到WidgetRevisions 将源2的翻译添加到WidgetRevisions 就这个问题而言,我只讨论在新的小部件上必须采取的前三个步骤。在此基础上,我假设第四步可以作为一个管道步骤

我正在使用Go语言进行数据导入工作,我希望将每个步骤编写为闭包,并使用通道进行通信,也就是说,每个步骤都是并发的。问题可以通过以下结构来定义

  • 从数据源获取小部件
  • 将源1的翻译添加到小部件
  • 将源代码2的翻译添加到小部件
  • 将定价从源1添加到小部件
  • 将WidgetRevisions添加到小部件。
  • 将源1的翻译添加到WidgetRevisions
  • 将源2的翻译添加到WidgetRevisions
  • 就这个问题而言,我只讨论在新的小部件上必须采取的前三个步骤。在此基础上,我假设第四步可以作为一个管道步骤来实现,它本身是按照一个三步以下的管道来实现的,以控制*WidgetRevision*s

    为此,我一直在编写一些代码,为我提供以下API:

    // A Pipeline is just a list of closures, and a smart 
    // function to set them all off, keeping channels of
    // communication between them.
    p, e, d := NewPipeline()
    
    // Add the three steps of the process
    p.Add(whizWidgets)
    p.Add(popWidgets)
    p.Add(bangWidgets)
    
    // Start putting things on the channel, kick off
    // the pipeline, and drain the output channel
    // (probably to disk, or a database somewhere)
    go emit(e)
    p.Execute()
    drain(d)
    
    我已经实现了它(代码位于或位于),但它死锁,成功率和失败率为100%

    调用
    p.Execute()
    时会出现死锁,原因可能是其中一个通道结束时无需执行任何操作,任何通道上都没有发送任何内容,也没有要执行的工作

    emit()
    drain()
    中添加几行调试输出,我看到了以下输出,我相信闭包调用之间的管道是正确的,我看到一些小部件被省略了

    Emitting A Widget
    Input Will Be Emitted On 0x420fdc80
    Emitting A Widget
    Emitting A Widget
    Emitting A Widget
    Output Will Drain From 0x420fdcd0
    Pipeline reading from 0x420fdc80 writing to 0x420fdd20
    Pipeline reading from 0x420fdd20 writing to 0x420fddc0
    Pipeline reading from 0x420fddc0 writing to 0x42157000
    
    关于这种方法,我知道以下几点:

    • 我相信这种设计会“饿死”一个或另一个协同程序的情况并不少见,我相信这就是为什么会出现死锁的原因
    • 我更希望管道首先有东西输入(API将实现
      pipeline.Process(*Widget)
      • 如果我能做到这一点,那么排水可能是一个“步骤”,它不会将任何内容传递给下一个函数,这可能是一个更干净的API
    • 我知道我还没有实现任何类型的梯级缓冲区,所以完全有可能只是让机器的可用内存过载
    • 我真的不相信这是一个好的围棋风格…但它似乎利用了很多围棋功能,但这不是一个真正的好处
    • 因为WidgetRevisions也需要一个管道,我想让我的管道更通用,也许一个
      接口{}
      类型是解决方案,我不知道是否足够好,以确定这是否合理
    • <> LI>我被建议考虑使用一个互斥体来防范竞争条件,但是我相信我是保存的,因为闭包将每个操作在WIDGET结构的一个特定单元上,但是我很乐意接受这个主题的教育。

    总而言之:我该如何修复这段代码,我该如何修复这段代码,如果你是一个比我更有经验的go程序员,你会如何解决“工作顺序单元”问题?

    我只是不认为我会构建远离通道的抽象。显式管道

    您可以非常轻松地为所有实际管道操作创建一个函数,如下所示:

    type StageMangler func(*Widget)
    
    func stage(f StageMangler, chi <-chan *Widget, cho chan<- *Widget) {
        for widget := range chi {
                    f(widget)
                    cho <- widget
        }
        close(cho)
    }
    
    type StageMangler func(*Widget)
    
    func stage(f StageMangler,chi我已经解决了死锁问题,问题是双重的,
    案例I==len(pipe.processs)
    是错误的,
    len()
    总是
    +1
    (新手错误)。我还将该条件实现为
    in,out=out,pipe.Drain
    ,而不是制作一个新的管道,这是愚蠢的…但关于这是一个体面的设计立场的问题。我觉得它有可能在我面前爆炸,并且类型不可重用,尽管我对管道机制非常满意…最终的代码h正在工作,但它迫切需要变得更通用。线索是,在每次运行中,
    行输出将从0x420fdcd0排出,具有相同的值(包括地址),但该地址不会在管道i/o输出中重复。这导致我查看
    Execute()的结束情况
    函数,我看到了我的错误。顺便说一句,使用
    var x=func()…
    有点像舞台。只需使用
    func x()
    。如果您能添加一些使用示例,那么我很乐意接受并支持您的答案,因为函数定义更简单,而且它不像我的实现那样依赖于每个阶段每个对象一个通道加上一个通道。每个阶段一个通道,每个通道都有一个goroutine进行处理,这没有什么错.我不知道该如何实现您正在做的事情,因为我不知道您的具体需求是什么。但是拥有一个
    *小部件
    输出通道,而不是从中读取,而是拥有另一件事情(或一组事情)从中阅读并输出到管道中的另一个
    *小部件
    输出通道对于像我这样的老UNIX程序员来说感觉非常自然。我想问你,你是否要实现上面的方法,它似乎还需要做很多通道处理。实际上,我想并行化一个连续的工作流,这是最终目标,哇,弹出一个d全部添加(远程查询)在实际示例中,将数据传输到小部件,并按顺序执行它们似乎是非常浪费的。我已经实现了类似的事情。如果您希望它们同时发生,您需要一种方法来同时更新它们,否则这非常简单。您确实需要将指针通过管道传输到循环中的每个通道,并且可能在小部件上有一个WaitGroup知道它什么时候被完全处理。