Взаимное исключение одновременных загрузок - PullRequest
2 голосов
/ 29 ноября 2011

В моем коде есть три одновременных подпрограммы. Я пытаюсь дать краткий обзор моего кода,

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
}

Я хочу, чтобы в то время, когда выполняются коды между двумя кодами (коды между двумя звездочками), поток управления не должен переходить к другим процедурам. Например, когда рутина 1 выполняет события между двумя звездами (отправка и печать событий), рутина 2 и 3 должна быть заблокирована (означает, что последовательность выполнения не переходит в рутину 2 или 3 из рутины 1). После завершения последнего события печати поток выполнения может перейти к процедуре 2 или 3. Кто-нибудь может мне помочь, уточнив, как мне этого добиться? Можно ли реализовать приведенную выше спецификацию с помощью WaitGroup ? Кто-нибудь может показать мне, приведя простой пример, как реализовать указанный выше пример с помощью WaitGroup. Спасибо.

Примечание: я даю две опции отправки и две печати, на самом деле отправка и печать много.

Ответы [ 4 ]

3 голосов
/ 30 ноября 2011

Если я правильно понял, вам нужно предотвратить одновременное выполнение какой-либо части каждой функции и других функций.Следующий код делает это: 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 в начале этого кода и разблокируйте в конце.Это то, что я сделал в соответствующих строках в Routine2 и Routine3.Надеюсь, что все проясняет.

2 голосов
/ 30 ноября 2011

Вы можете использовать sync.Mutex.Например, все, что находится между im.Lock() и im.Unlock(), исключает другую программу.

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 голосов
/ 14 августа 2013

Другим способом было бы иметь канал управления, с возможностью выполнения только одной горутины в любой момент времени, при этом каждая подпрограмма отправлялась обратно в «контрольную блокировку» всякий раз, когда они выполняли свою атомарную операцию:

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

Это печатает:

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 голосов
/ 29 ноября 2011

Вы запрашиваете библиотечную функцию, которая явно останавливает другие процедуры?Дайте понять, что это не возможно в Go, а также для большинства других языков.И синхронизировать, случай мьютекса для вашего случая невозможен.

...