并发 Goroutines 的互斥

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

Mutual Exclusion of Concurrent Goroutines

问题

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

例程1 {
做一些事情

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

做一些事情
}

例程2 {
做一些事情

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

做一些事情
}

例程3 {
做一些事情

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

做一些事情
}

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

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

注:我给出了两个发送和两个打印选项,实际上有很多发送和打印。

英文:

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 goroutines. 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: I give two sending and two printing options, in fact there is lots of sending and printing.

答案1

得分: 3

如果我理解正确,你想要的是防止某些函数的某个部分与其他函数同时执行。以下代码可以实现这一点:当其他例程正在运行时,fmt.Println行不会执行。具体操作如下:当执行到打印部分时,如果其他例程正在运行,则等待它们结束,而在执行此打印行时,其他例程不会启动并等待。希望这就是你要找的。如果我对此理解有误,请纠正我。

package main

import (
    "fmt"
    "rand"
    "sync"
)

var (
    mutex1, mutex2, mutex3 sync.Mutex
    wg sync.WaitGroup
)

func Routine1() {
    mutex1.Lock()
    // do something
    for i := 0; i < 200; i++ {
        mutex2.Lock()
        mutex3.Lock()
        fmt.Println("value of z")
        mutex2.Unlock()
        mutex3.Unlock()
    }
    // do something
    mutex1.Unlock()
    wg.Done()
}

func Routine2() {
    mutex2.Lock()
    // do something
    for i := 0; i < 200; i++ {
        mutex1.Lock()
        mutex3.Lock()
        fmt.Println("value of z")
        mutex1.Unlock()
        mutex3.Unlock()
    }
    // do something
    mutex2.Unlock()
    wg.Done()
}

func Routine3() {
    mutex3.Lock()
    // do something
    for i := 0; i < 200; i++ {
        mutex1.Lock()
        mutex2.Lock()
        fmt.Println("value of z")
        mutex1.Unlock()
        mutex2.Unlock()
    }
    // do something
    mutex3.Unlock()
    wg.Done()
}

func main() {
    wg.Add(3)
    go Routine1()
    go Routine2()
    Routine3()
    wg.Wait()
}

更新:让我解释一下这三个互斥锁:互斥锁是指当你在一个互斥锁上调用Lock时,如果有其他人锁定了相同的互斥锁,你的代码就会在那里等待。当你调用Unlock后,被阻塞的代码将会继续执行。

在这里,我通过在函数开头锁定互斥锁并在结尾解锁来将每个函数放在自己的互斥锁中。通过这个简单的机制,你可以避免在这些函数运行时同时运行任何你想要避免的代码部分。例如,无论何时你想要在Routine1运行时不运行的代码,只需在该代码的开头锁定mutex1,并在结尾解锁。这就是我在Routine2Routine3中适当的行中所做的。希望这样能澄清问题。

英文:

If I have gotten it correctly, what you want is to prevent simultaneous execution of some part of each function and other functions. The following code does this: fmt.Println lines won't happen as other routines are running. Here's what happens: when execution gets to the print section, it waits until other routines end, if they are running, and while this print line is executing other routines don't start and wait. I hope that is what you are looking for. Correct me if I'm wrong about this.

package main
import (
&quot;fmt&quot;
&quot;rand&quot;
&quot;sync&quot;
)
var (
mutex1, mutex2, mutex3 sync.Mutex
wg sync.WaitGroup
)
func Routine1() {
mutex1.Lock()
// do something
for i := 0; i &lt; 200; i++ {
mutex2.Lock()
mutex3.Lock()
fmt.Println(&quot;value of z&quot;)
mutex2.Unlock()
mutex3.Unlock()
}
// do something
mutex1.Unlock()
wg.Done()
}
func Routine2() {
mutex2.Lock()
// do something
for i := 0; i &lt; 200; i++ {
mutex1.Lock()
mutex3.Lock()
fmt.Println(&quot;value of z&quot;)
mutex1.Unlock()
mutex3.Unlock()
}
// do something
mutex2.Unlock()
wg.Done()
}
func Routine3() {
mutex3.Lock()
// do something
for i := 0; i &lt; 200; i++ {
mutex1.Lock()
mutex2.Lock()
fmt.Println(&quot;value of z&quot;)
mutex1.Unlock()
mutex2.Unlock()
}
// do something
mutex3.Unlock()
wg.Done()
}
func main() {
wg.Add(3)
go Routine1()
go Routine2()
Routine3()
wg.Wait()
}

UPDATE: Let me explain these three mutex here: a mutex is, as documentation says: "a mutual exclusion lock." That means when you call Lock on a mutex your code just waits there if somebody else have locked the same mutex. Right after you call Unlock that blocked code will be resumed.

Here I put each function in its own mutex by locking a mutex at the beginning of the function and unlocking it the end. By this simple mechanism you can avoid running any part of code you want at the same time as those functions. For instance, everywhere you want to have a code that should not be running when Routine1 is running, simply lock mutex1 at the beginning of that code and unlock at at the end. That is what I did in appropriate lines in Routine2 and Routine3. Hope that clarifies things.

答案2

得分: 2

你可以使用sync.Mutex。例如,在im.Lock()im.Unlock()之间的所有内容都将排除其他goroutine。

package main
import (
"fmt"
"sync"
)
func f1(wg *sync.WaitGroup, im *sync.Mutex, i *int) {
for {
im.Lock()
(*i)++
fmt.Printf("Go1: %d\n", *i)
done := *i >= 10
im.Unlock()
if done {
break
}
}
wg.Done()
}
func f2(wg *sync.WaitGroup, im *sync.Mutex, i *int) {
for {
im.Lock()
(*i)++
fmt.Printf("Go2: %d\n", *i)
done := *i >= 10
im.Unlock()
if done {
break
}
}
wg.Done()
}
func main() {
var wg sync.WaitGroup
var im sync.Mutex
var i int
wg.Add(2)
go f1(&wg, &im, &i)
go f2(&wg, &im, &i)
wg.Wait()   
}
英文:

You could use sync.Mutex. For example everything between im.Lock() and im.Unlock() will exclude the other goroutine.

package main
import (
&quot;fmt&quot;
&quot;sync&quot;
)
func f1(wg *sync.WaitGroup, im *sync.Mutex, i *int) {
for {
im.Lock()
(*i)++
fmt.Printf(&quot;Go1: %d\n&quot;, *i)
done := *i &gt;= 10
im.Unlock()
if done {
break
}
}
wg.Done()
}
func f2(wg *sync.WaitGroup, im *sync.Mutex, i *int) {
for {
im.Lock()
(*i)++
fmt.Printf(&quot;Go2: %d\n&quot;, *i)
done := *i &gt;= 10
im.Unlock()
if done {
break
}
}
wg.Done()
}
func main() {
var wg sync.WaitGroup
var im sync.Mutex
var i int
wg.Add(2)
go f1(&amp;wg, &amp;im, &amp;i)
go f2(&amp;wg, &amp;im, &amp;i)
wg.Wait()   
}

答案3

得分: 0

另一种方法是使用一个控制通道,每次只允许一个goroutine执行,每个例程在完成原子操作后都会发送回“控制锁”:

package main
import "fmt"
import "time"
func routine(id int, control chan struct{}){
for {
// 获取控制权
<-control
fmt.Printf("例程 %d 获取了控制权\n", id)
fmt.Printf("这里发生了很多事情...")
time.Sleep(1)
fmt.Printf("...但只有例程 %d 执行!\n", id)
fmt.Printf("例程 %d 归还了控制权\n", id)
// 将控制权发送回其他捕获它的例程
control<-struct{}{}
}
}
func main() {
// 控制通道是阻塞的
control := make(chan struct{})
// 启动所有例程
go routine(0, control)
go routine(1, control)
go routine(2, control)
// 将控制权发送给第一个捕获到它的例程
control<-struct{}{}
// 让例程运行一段时间...
time.Sleep(10)
// 获取控制权并终止
<-control
close(control)
fmt.Println("完成!")
}

这将打印出:

例程 0 获取了控制权
这里发生了很多事情...但只有例程 0 执行!
例程 0 归还了控制权
例程 1 获取了控制权
这里发生了很多事情...但只有例程 1 执行!
例程 1 归还了控制权
例程 2 获取了控制权
这里发生了很多事情...但只有例程 2 执行!
例程 2 归还了控制权
例程 0 获取了控制权
这里发生了很多事情...但只有例程 0 执行!
例程 0 归还了控制权
例程 1 获取了控制权
这里发生了很多事情...但只有例程 1 执行!
例程 1 归还了控制权
例程 2 获取了控制权
这里发生了很多事情...但只有例程 2 执行!
例程 2 归还了控制权
例程 0 获取了控制权
这里发生了很多事情...但只有例程 0 执行!
例程 0 归还了控制权
例程 1 获取了控制权
这里发生了很多事情...但只有例程 1 执行!
例程 1 归还了控制权
例程 2 获取了控制权
这里发生了很多事情...但只有例程 2 执行!
例程 2 归还了控制权
例程 0 获取了控制权
这里发生了很多事情...但只有例程 0 执行!
例程 0 归还了控制权
例程 1 获取了控制权
这里发生了很多事情...但只有例程 1 执行!
例程 1 归还了控制权
例程 2 获取了控制权
这里发生了很多事情...但只有例程 2 执行!
例程 2 归还了控制权
完成!
英文:

Another way would be to have a control channel, with only one goroutine allowed to execute at any one time, with each routine sending back to the 'control lock' whenever they are done doing their atomic operation:

package main
import &quot;fmt&quot;
import &quot;time&quot;
func routine(id int, control chan struct{}){
for {
// Get the control
&lt;-control
fmt.Printf(&quot;routine %d got control\n&quot;, id)
fmt.Printf(&quot;A lot of things happen here...&quot;)
time.Sleep(1)
fmt.Printf(&quot;... but only in routine %d !\n&quot;, id)
fmt.Printf(&quot;routine %d gives back control\n&quot;, id)
// Sending back the control to whichever other routine catches it
control&lt;-struct{}{}
}
}
func main() {
// Control channel is blocking
control := make(chan struct{})
// Start all routines
go routine(0, control)
go routine(1, control)
go routine(2, control)
// Sending control to whichever catches it first
control&lt;-struct{}{}
// Let routines play for some time...
time.Sleep(10)
// Getting control back and terminating
&lt;-control
close(control)
fmt.Println(&quot;Finished !&quot;)
}

This prints:

routine 0 got control
A lot of things happen here...... but only in routine 0 !
routine 0 gives back control
routine 1 got control
A lot of things happen here...... but only in routine 1 !
routine 1 gives back control
routine 2 got control
A lot of things happen here...... but only in routine 2 !
routine 2 gives back control
routine 0 got control
A lot of things happen here...... but only in routine 0 !
routine 0 gives back control
routine 1 got control
A lot of things happen here...... but only in routine 1 !
routine 1 gives back control
routine 2 got control
A lot of things happen here...... but only in routine 2 !
routine 2 gives back control
routine 0 got control
A lot of things happen here...... but only in routine 0 !
routine 0 gives back control
routine 1 got control
A lot of things happen here...... but only in routine 1 !
routine 1 gives back control
routine 2 got control
A lot of things happen here...... but only in routine 2 !
routine 2 gives back control
routine 0 got control
A lot of things happen here...... but only in routine 0 !
routine 0 gives back control
routine 1 got control
A lot of things happen here...... but only in routine 1 !
routine 1 gives back control
routine 2 got control
A lot of things happen here...... but only in routine 2 !
routine 2 gives back control
Finished !

答案4

得分: -3

你正在寻找一个明确停止其他例程的库函数?明确一点,这在Go语言和大多数其他语言中是不可能的。对于你的情况,sync、mutex也是不可能的。

英文:

You are asking for a library function that explicitly stop other routines ? Make it clear that, it is not possible in Go and also for most other languages. And sync, mutex case is not possible for your case.

huangapple
  • 本文由 发表于 2011年11月30日 02:41:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/8315970.html
匿名

发表评论

匿名网友

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

确定