Mutual Exclusion of Concurrent Goroutines

后端 未结 4 2118
天命终不由人
天命终不由人 2020-12-18 13:56

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 Routi         


        
相关标签:
4条回答
  • 2020-12-18 14:26

    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 (
        "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()
    }
    

    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.

    0 讨论(0)
  • 2020-12-18 14:31

    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 "fmt"
    import "time"
    
    func routine(id int, control chan struct{}){
        for {
            // Get the control
            <-control
            fmt.Printf("routine %d got control\n", id)
            fmt.Printf("A lot of things happen here...")
            time.Sleep(1)
            fmt.Printf("... but only in routine %d !\n", id)
            fmt.Printf("routine %d gives back control\n", id)
            // Sending back the control to whichever other routine catches it
            control<-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<-struct{}{}
        // Let routines play for some time...
        time.Sleep(10)
        // Getting control back and terminating
        <-control
        close(control)
        fmt.Println("Finished !")
    }
    

    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 !
    
    0 讨论(0)
  • 2020-12-18 14:37

    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.

    0 讨论(0)
  • 2020-12-18 14:51

    You could use sync.Mutex. For example everything between im.Lock() and im.Unlock() will exclude the other 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()   
    
    }
    
    0 讨论(0)
提交回复
热议问题