Heim >Backend-Entwicklung >Golang >Warum kommt es zu einem Deadlock, wenn der Funktionsaufruf, der den Kanal füllt, nicht in eine Goroutine eingebettet ist?

Warum kommt es zu einem Deadlock, wenn der Funktionsaufruf, der den Kanal füllt, nicht in eine Goroutine eingebettet ist?

王林
王林nach vorne
2024-02-10 12:00:10763Durchsuche

当填充通道的函数调用未嵌入 Goroutine 中时,为什么会出现死锁?

Wenn der Funktionsaufruf, der den Kanal füllt, nicht in eine Goroutine eingebettet ist, liegt der Grund für den Deadlock darin, dass die Sende- und Empfangsvorgänge des Kanals blockieren. Wenn die Funktion, die den Kanal füllt, in der Haupt-Goroutine aufgerufen wird und der Füllvorgang nicht in eine neue Goroutine eingefügt wird, um innerhalb der Funktion ausgeführt zu werden, wartet die Haupt-Goroutine darauf, dass der Kanal über genügend Platz zum Empfangen von Daten verfügt Der Füllvorgang kann nicht durchgeführt werden, was zu einem Deadlock führt. Um einen Deadlock zu vermeiden, müssen wir daher Goroutine für die gleichzeitige Ausführung beim Füllvorgang des Kanals verwenden, um sicherzustellen, dass der Füllvorgang und der Empfangsvorgang gleichzeitig ausgeführt werden können.

Frageninhalt

Ich kenne die Option sync 包及其 waitgroup und möchte sie für diesen Test nicht verwenden. Ich teste einen Semaphor.

Also ich habe:

package main

import (
    "fmt"
    "os"
    "time"
)

func main() {

    fmt.print("wassap")

    jobs := make(chan int)
    processstarted := make(chan struct{}, 1)
    processcompleted := make(chan struct{}, 1)

    createjobs(jobs)

    go func() {
        worker(jobs, processstarted, processcompleted)
    }()

    go func() {
        sync(processstarted, processcompleted)
    }()

    time.sleep(3600 * time.second)
    fmt.print("\nend of main...")

    interrupt := make(chan os.signal)
    <-interrupt

}

func createjobs(jobs chan<- int) {
    defer close(jobs)
    for i := 1; i < 20; i++ {
        jobs <- i
    }
}

func worker(jobs <-chan int, processstarted <-chan struct{}, processcompleted <-chan struct{}) {

    for {
        select {
        case i := <-jobs:
            fmt.printf("\nfetching job #%d from channel", i)
            time.sleep(2 * time.second)
        case <-processstarted:
            fmt.print("\nprocess started. waiting for it to be completed")
            <-processcompleted
            fmt.print("\nprocess completed")
        }

    }
}

func sync(processstarted chan<- struct{}, processcompleted chan<- struct{}) {

    // acquire semaphore. send signal to channel to indicate that it is busy
    processstarted <- struct{}{}

    for i := 1; i < 5; i++ {
        fmt.printf("\nprocessing %d", i)
        time.sleep(5 * time.second)
    }

    // release semaphore
    processcompleted <- struct{}{}
}

Was ich testen möchte, ist ganz einfach: Ich habe ein createjobs 函数,其唯一目的是将元素添加到通道,在本例中是一个 int 通道。然后我有一个 worker, das ein Objekt aus diesem Kanal extrahiert und 2 Sekunden lang schläft, bevor es das nächste Element extrahiert.

Jetzt gibt es auch eine Synchronisierungsfunktion. Der einzige Zweck dieser Funktion besteht darin, das worker 运行时启动的进程。如果此进程处于活动状态,则在 sync 结束时应停止处理 jobs-Element zu simulieren. Deshalb habe ich zwei Kanäle, von denen einer anzeigt, dass der Prozess gestartet wurde, und der andere, dass der Prozess beendet ist.

Ich erhalte beim Ausführen meines Codes die folgende Fehlermeldung:

fatal error: all goroutines are asleep - deadlock!

Wenn ich die Art und Weise ändere, wie createjobs aufgerufen wird, wickle es in eine Goroutine wie diese ein:

go func() {
        createJobs(jobs)
    }()

Dann läuft mein Code korrekt.

Ich möchte nur verstehen, warum das passiert. Was ich meine ist: Die main-Routine wird ausgeführt und ruft dann main 例程正在执行,然后它调用 createjobs (无换行),因此 main 例程应该被阻止,直到此调用结束。一旦 createjobs 结束,就说明通道中有元素了。 main 继续执行并启动其他 goroutine workersync 来完成它们的工作。在 main (ohne Zeilenumbruch) auf, daher sollte die main-Routine blockiert werden, bis dieser Aufruf endet. Sobald

endet, bedeutet dies, dass sich ein Element im Kanal befindet. main setzt die Ausführung fort und startet andere Goroutinen worker und sync, um ihre Arbeit abzuschließen. Bevor main endet, füge ich einfach einen Sleeper hinzu, um der zuvor erstellten Goroutine Zeit zum Abschluss zu geben.

createjobs 发生在 goroutine 之外时会发生什么。

解决方法

您将 jobs 声明为无缓冲通道,然后尝试将 20 个值同步推入其中。当您调用 createjobs(jobs)Ich frage nicht nach anderen Lösungen für dieses Problem, ich möchte nur wissen, was passiert, wenn

außerhalb einer Goroutine passiert.

Workaround

Sie deklarieren jobs als ungepufferten Kanal und versuchen dann, 20 Werte synchron hineinzuschieben. Dadurch wird Ihre Hauptfunktion blockiert, wenn Sie

aufrufen. Ändern Sie Zeile 13 in:

    jobs := make(chan int, 20)

...wird die Sackgasse lösen.

EDIT – Klarstellung in Kommentaren erbeten:

createjobs(jobs)

Ungepufferte Kanäle haben keine Kapazität und blockieren die Ausführung des Produzenten, bis der Verbraucher die Nachricht erhält.

Eine gute Analogie für einen ungepufferten Kanal ist eine Pipe. In diesem Fall sieht der Prozess so aus:

+------------------+     +------------+      +-------------+
| PRODUCER         |     | PIPE       |      | CONSUMER    |
|                  +---->|            +----->|             |
| createJobs(jobs) |     | unbuffered |      | worker(...) |
|                  |     | channel    |      |             |
+------------------+     +------------+      +-------------+

Der Deadlock tritt auf, weil

synchron aufgerufen wird und noch kein Consumer läuft. main()

Es funktioniert beim Aufrufen einer Funktion (🎜Produzent🎜) in einer Goroutine, weil das Einfügen des Kanals und das Lesen des Kanals grundsätzlich parallel erfolgen? 🎜 🎜Ja. Wenn der Produzent asynchron aufgerufen wird, wird die Funktion 🎜 nicht blockiert, sodass auch der Verbraucher eine Chance hat, aufgerufen zu werden. In diesem Fall wird der Produzent alle seine Aufgaben nacheinander vorantreiben, so wie die Arbeiter sie nacheinander verbrauchen. 🎜

Das obige ist der detaillierte Inhalt vonWarum kommt es zu einem Deadlock, wenn der Funktionsaufruf, der den Kanal füllt, nicht in eine Goroutine eingebettet ist?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen