Самосинхронизирующиеся рутины заканчиваются тупиком - PullRequest
0 голосов
/ 03 ноября 2018

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

Чтобы быть более конкретным: Я должен выполнить задачу, в которой мне нужно запустить большое количество потоков (в этом примере только с двумя потоками) в основной процедуре. Предполагается, что все инициированные работники должны самостоятельно подготовить некоторые действия по инициализации. Пока они не достигнут небольшой последовательности команд, которую я хочу, чтобы они выполнялись всеми программами одновременно, поэтому я хочу самосинхронизировать эти программы друг с другом. Для моей задачи очень важно, чтобы задержка выполнения основной подпрограммы, которая создает экземпляры всех других подпрограмм, не влияла на истинный параллелизм выполнения рабочих (на ярлыке #maximum параллельно в комментарии). Для этого я инициализирую группу ожидания с количеством запущенных подпрограмм в основной подпрограмме и передаю ее всем подпрограммам, чтобы они могли синхронизировать рабочий процесс друг друга.

Код выглядит примерно так:

import sync

func worker_action(wait_group *sync.WaitGroup) {
    // ...
    // initialization
    // ...

    defer wait_group.Done() 
    wait_group.Wait() // #label: wait

    // sequence of maximum parallel instructions // #label: maximum parallel

    // ...
}

func main() {
    var numThreads int = 2 // the number of threads shall be much higher for the actual stress test

    var wait_group sync.WaitGroup
    wait_group.Add(numThreads)
    for i := 0; i < numThreads; i++ {
        go worker_action(&wait_group)
    }

    // ...
}

К сожалению, моя установка заходит в тупик, как только все goroutines достигли инструкции Wait (помеченной в комментарии #wait). Это верно для любого количества потоков, которые я запускаю с основной подпрограммы (даже два потока за короткое время оказываются в тупике).

С моей точки зрения, тупик не должен возникать из-за того, что непосредственно перед инструкцией ожидания каждая goroutine выполняет функцию done в той же группе ожидания.

Я неправильно понимаю, как работают группы ожидания? Например, нельзя ли выполнять функцию ожидания внутри программы, отличной от основной процедуры? Или может кто-нибудь подсказать мне, что еще мне не хватает?

Большое спасибо заранее.

EDIT:

Большое спасибо @tkausl. Это была действительно ненужная «отсрочка», которая вызвала проблему. Я не знаю, как я не мог видеть это сам.

Ответы [ 2 ]

0 голосов
/ 03 ноября 2018

В вашем коде есть несколько проблем. Сначала форма. Idiomatic Go должен использовать camelCase. wg - это лучшее название для WaitGroup.

Но более важным является использование там, где ваш код ожидает. Не внутри твоих Goroutines. Следует подождать внутри основного функционала:

func workerAction(wg *sync.WaitGroup) {
    // ...
    // initialization
    // ...

    defer wg.Done() 
    // wg.Wait() // #label: wait

    // sequence of maximum parallel instructions // #label: maximum parallel

    // ...
}

func main() {
    var numThreads int = 2 // the number of threads shall be much higher for the actual stress test

    var wg sync.WaitGroup
    wg.Add(numThreads)
    for i := 0; i < numThreads; i++ {
        go workerAction(&wg)
    }
    wg.Wait() // you need to wait here

    // ...
}
0 голосов
/ 03 ноября 2018

Еще раз спасибо @tkausl. Эта проблема была решена путем удаления ненужной инструкции «defer» из строки, которая должна была позволить рабочим процедурам увеличивать число законченных потоков.

т.е. "defer wait_group.Done ()" -> "wait_group.Done ()"

...