并发Go协程的相互执行问题

huangapple go评论81阅读模式
英文:

Issue with Mutual Execution of Concurrent Go Routines

问题

在我的代码中有三个并发例程。我试图简要概述我的代码,

例程1 {
做一些事情

向例程2发送整数
向例程3发送整数
打印一些内容
打印一些内容

做一些事情
}

例程2 {
做一些事情

向例程1发送整数
向例程3发送整数
打印一些内容
打印一些内容

做一些事情
}

例程3 {
做一些事情

向例程1发送整数
向例程2发送整数
打印一些内容
打印一些内容

做一些事情
}

主函数 {
例程1
例程2
例程3
}

我希望,在两个"做一些事情"之间的代码(在两个星号之间的代码)执行时,控制流不会转到其他例程。例如,当例程1执行两个星号之间的事件(发送和打印事件)时,例程2和3必须被阻塞(意味着执行流不会从例程1传递到例程2或3)。在完成最后一个打印事件后,执行流可以传递到例程2或3。有人可以通过指定如何实现这一点来帮助我吗?是否可以通过WaitGroup来实现上述规范?有人可以通过给出一个简单的例子来展示如何使用WaitGroup来实现上述指定的例子吗?谢谢。

注:这可能是这个问题的重复问题。我尝试使用同步锁机制,但可能是因为我的代码很大,所以我无法正确地放置锁定和解锁,这导致死锁情况(或者可能是我的方法错误产生)。有人可以通过一个简单的步骤帮助我实现这个吗?我在这里给出了我的代码的一个简单示例,在这里我想将两个打印和发送事件放在互斥锁内(对于例程1),以便例程2无法中断它。你能帮我解决这个问题吗?一个可能的解决方案是,http://play.golang.org/p/-uoQSqBJKS,但会出现错误。

英文:

In my code there are three concurrent routines. I try to give a brief overview of my code,

Routine 1 {
do something

*Send int to Routine 2
Send int to Routine 3
Print Something
Print Something*

do something
}

Routine 2 {
do something

*Send int to Routine 1
Send int to Routine 3
Print Something
Print Something*

do something
}

Routine 3 {
do something

*Send int to Routine 1
Send int to Routine 2
Print Something
Print Something*

do something
}

main {
routine1
routine2
routine3
}

I want that, while codes between two do something (codes between two star marks) is executing, flow of control must not go to other go routines. For example, when routine1 is executing the events between two stars (sending and printing events), routine 2 and 3 must be blocked (means flow of execution does not pass to routine 2 or 3 from routine 1).After completing last print event, flow of execution may pass to routine 2 or 3.Can anybody help me by specifying, how can I achieve this ? Is it possible to implement above specification by WaitGroup ? Can anybody show me by giving a simple example how to implement above specified example by using WaitGroup. Thanks.

NB:May be this is repeat question of this. I tried by using that sync-lock mechanism, however, may be because I have a large code that's why I could not put lock-unlock properly, and it's creating deadlock situation (or may be my method is error producing). Can anybody help me by a simple procedure thus I can achieve this. I give a simple example of my code here where Here I want to put two prints and sending event inside mutex (for routine 1) thus routine 2 can't interrupt it. Can you help me how is it possible. One possible solution given,
http://play.golang.org/p/-uoQSqBJKS which gives error.

答案1

得分: 3

死锁问题是,如果不允许其他goroutine被调度,那么你的通道发送无法继续进行,除非有缓冲区。Go的通道具有有限的缓冲区,因此在满载之前,你会遇到在排空之前发送的竞争条件。你可以引入无限缓冲区,或者将每个发送放在自己的goroutine中,但归根结底是:你为什么要这样做;你想要实现什么?

另外一件事:如果你只想确保在*之间的三组代码的互斥性,那么是的,你可以使用互斥锁。如果你想确保没有代码中断你的块,无论它在哪里被挂起,那么你可能需要使用runtime.LockOSThread和runtime.UnlockOSThread。这些是相当底层的,你需要知道自己在做什么,而且它们很少需要。如果你希望没有其他goroutine在运行,你将需要runtime.GOMAXPROCS(1),这是当前的默认值。

英文:

Why do you want to do this?

The deadlock problem is, if you don't allow other goroutines to be scheduled, then your channel sends can't proceed, unless there's buffering. Go's channels have finite buffering, so you end up with a race condition on draining before they get sent on while full. You could introduce infinite buffering, or put each send in its own goroutine, but it again comes down to: why are you trying to do this; what are you trying to achieve?

Another thing: if you only want to ensure mutual exclusion of the three sets of code between *s, then yes, you can use mutexes. If you want to ensure that no code interrupts your block, regardless of where it was suspended, then you might need to use runtime.LockOSThread and runtime.UnlockOSThread. These are fairly low level and you need to know what you're doing, and they're rarely needed. Of you want there to be no other goroutines running, you'll have to have runtime.GOMAXPROCS(1), which is currently the default.

答案2

得分: 3

回答你的问题的问题在于似乎没有人真正理解你的问题是什么。我看到你一再询问大致相同的问题,尽管没有取得任何进展。这并不是冒犯之意。这是一种试图通过建议重新表述你的问题以便他人理解来帮助你的尝试。作为一个可能的好处,有些问题在以一种可理解的方式向他人解释时会自行解决。我自己也经历过很多次这样的情况。

另一个提示可能在于明显的同步和通道通信的混合。这并不意味着设计一定有问题。只是在典型/简单的情况下不会发生这种情况。再次强调,你的问题可能是非典型/非平凡的。

也许可以通过只使用通道来重新设计你的问题。实际上,我相信每个涉及显式同步(在Go中)的问题都可以使用只使用通道来编码。也就是说,确实有些问题使用显式同步非常容易。而且通道通信虽然便宜,但并不像大多数同步原语那样便宜。但这可以在代码工作后再考虑,如果代码工作正常并且希望在进行调整时有测试来监视你的步骤,那么切换到某种同步原语(比如sync.Mutex)应该是可能的,而且更容易做到。

试着将你的goroutine想象成独立行动的代理,它们:

  • 独占从通道接收到的数据。语言不会强制执行这一点,你必须自己遵守纪律。
  • 不再触碰它们发送到通道的数据。这是从第一条规则中得出的,但重要到足以明确表述。
  • 通过数据类型与其他代理(goroutine)进行交互,这些数据类型封装了整个工作流/计算的一个完整单元。这消除了你之前在完成“单元”之前获取正确数量的通道消息时的困扰。
  • 对于它们使用的每个通道,在使用之前必须绝对清楚该通道是否必须是无缓冲的、必须为固定数量的项目缓冲或者是否可以是无限制的。
  • 不必考虑(知道)其他代理在做什么,只需从它们那里获取一条消息,如果这对代理完成自己的任务-作为更大的画面的一部分-是必要的。

即使使用这些经验法则,希望能够产生更容易理解的代码,并且通常不需要任何其他同步。(我故意忽略了关键应用程序的性能问题。)

英文:

The problem in answering your question is that it seems no one understands what your problem really is. I see you're asking repeatedly about roughly the same, though no progress has been done. There's no offense in saying this. It's an attempt to help you by a suggestion to reformulate your problem in a way comprehensible to others. As a possible nice side effect, some problems do solve themselves while being explained to others in an understandable way. I've experienced that many times by myself.

Another hint could be in the suspicious mix of explicit syncing and channel communication. That doesn't mean the design is necessarily broken. It just doesn't happen in a typical/simple case. Once again, your problem might be atypical/non trivial.

Perhaps it's somehow possible to redesign your problem using only channels. Actually I believe that every problem involving explicit synchronization (in Go) could be coded while using only channels. That said, it is true some problems are written with explicit synchronization very easily. Also channel communication, as cheap as it is, is not as cheap as most synchronization primitives. But that could be looked after later, when the code works. If the "pattern" for some say sync.Mutex will visibly emerge in the code, it should be possible to switch to it and much more easy to do that when the code already works and hopefully has tests to watch your steps while making the adjustments.

Try to think about your goroutines like independently acting agents which:

  • Exclusively own the data received from the channel. The language will
    not enforce this, you must deploy own's discipline.
  • Don't anymore touch the data they've sent to a channel. It follows from first rule, but important enough to be explicit.
  • Interact with other agents (goroutines) by data types, which encapsulate a whole unit of workflow/computation. This eliminates e.g. your earlier struggle with geting the right number of channel messages before the "unit" is complete.
  • For every channel they use, it must be absolutely clear in before if the channel must be unbuffered, must be buffered for fixed number of items or if it may be unbound.
  • Don't have to think (know) about what other agents are doing above getting a message from them if that is needed for the agent to do its own task - part of the bigger picture.

Using even such few rules of thumb should hopefully produce code which is more easy to reason about and which usually doesn't requires any other synchronization. (I'm intentionally ignoring performance issues of mission critical applications now.)

huangapple
  • 本文由 发表于 2011年12月4日 12:46:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/8373329.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定