Как я узнаю, что все мои программы действительно ожидают условия, используя пакет синхронизации golang? - PullRequest
2 голосов
/ 14 октября 2019

У меня есть приложение, в котором я создаю несколько программ для одновременного выполнения определенной задачи. Все рабочие программы ожидают наступления условия / события, и как только событие инициируется, они начинают свое выполнение. После создания всех подпрограмм главный поток должен знать, что перед отправкой широковещательного сигнала все подпрограммы действительно находятся в состоянии ожидания.

Я знаю, что это можно сделать с помощью каналов (что рекомендуется), но я также обнаружил, чтопакет синхронизации go интересный. Просто пытаюсь понять, как добиться той же функциональности, используя пакет синхронизации вместо каналов

package main

import (
    "fmt"
    "sync"
    "time"
)

var counter int

func worker(wg *sync.WaitGroup, cond *sync.Cond, id int) {
    fmt.Println("Starting Goroutine ID:", id)
    // Get a lock and wait
    cond.L.Lock()
    defer cond.L.Unlock()
    fmt.Println("Goroutine with ID: ", id, "obtained a lock")
    // Do some processing with the shared resource and wait
    counter++
    wg.Done()
    cond.Wait()
    fmt.Println("Goroutine ID:", id, "signalled. Continuing...")
}

func main() {
    var wg sync.WaitGroup
    cond := sync.NewCond(&sync.Mutex{})
    counter = 0
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(&wg, cond, i)
    }

    wg.Wait() // Wait()'ing only until the counter is incremented
    // How to make sure that all goroutines you created are indeed wait()'ing ?????
    cond.Broadcast()
    time.Sleep(2 * time.Second)
    cond.Broadcast()
    fmt.Println("Final value of the counter is", counter)
}

Если я не использую приведенные ниже операторы в последних 3 строках (кроме fmt.Println)

    time.Sleep(2 * time.Second)
    cond.Broadcast()

Я получаю следующий вывод ..

Starting Goroutine ID: 4
Goroutine with ID:  4 obtained a lock
Starting Goroutine ID: 3
Goroutine with ID:  3 obtained a lock
Starting Goroutine ID: 1
Goroutine with ID:  1 obtained a lock
Starting Goroutine ID: 0
Goroutine with ID:  0 obtained a lock
Starting Goroutine ID: 2
Goroutine with ID:  2 obtained a lock
Final value of the counter is 5
Goroutine ID: 3 signalled. Continuing...

В идеале каждая goroutine должна иметь возможность печатать

Goroutine ID: 3 signalled. Continuing...

с соответствующим идентификатором goroutine. Мы не можем напечатать это, потому что не все goroutines сигнализируются, поскольку некоторые из них даже не находятся в состоянии ожидания. Вот почему я добавил время. Сон, который не является практическим решением.

Мой вопрос ... Как я могу знать, что все goroutines фактически ожидают при условии cond.Wait () .. Каналырешение, но я хочу знать, как я мог бы сделать это с помощью пакета синхронизации Go?

Ответы [ 2 ]

1 голос
/ 14 октября 2019

У вас проблема в том, что Broadcast не гарантирует пробуждение всех процедур, так как он вызывает только те программы, которые уже ожидают, и между wg.Done() и cond.Wait() есть небольшое окно. Обычно переменная условия будет использоваться с чем-то, что представляет «условие», которое вы используете для синхронизации. В этом случае у вас может быть переменная пакета bool, которая указывает, можно ли продолжить выполнение процедур. main установит это, а затем сделает трансляцию, чтобы сказать, что горутины продолжаются. Например:

package main

    import (
    "fmt"
    "sync"
)

var counter int
var start bool

func worker(wg *sync.WaitGroup, cond *sync.Cond, id int) {
    fmt.Println("Starting Goroutine ID:", id)
    // Get a lock and wait
    cond.L.Lock()
    fmt.Println("Goroutine with ID: ", id, "obtained a lock")
    // Do some processing with the shared resource and wait
    counter++
    if !start {
        cond.Wait()
    }
    cond.L.Unlock()
    fmt.Println("Goroutine ID:", id, "signalled. Continuing...")
    wg.Done() // Worker is completely done
}

func main() {
    var wg sync.WaitGroup
    cond := sync.NewCond(&sync.Mutex{})
    counter = 0
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(&wg, cond, i)
    }

    cond.L.Lock()
    start = true
    cond.Broadcast()
    cond.L.Unlock()

    wg.Wait() // Wait until all workers are done
    fmt.Println("Final value of the counter is", counter)
}

Добавление переменной start делает невозможным продолжение выполнения процедур, когда main сообщает им.

1 голос
/ 14 октября 2019

Все твои горутины проснулись. Тем не менее, ваша программа завершает работу, прежде чем они могут работать. Вам не нужна вторая трансляция. Достаточно просто подождать после первой трансляции, чтобы у подпрограмм была возможность запустить программу до завершения программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...