Rumah >pembangunan bahagian belakang >Golang >Mengapakah kebuntuan berlaku apabila panggilan fungsi yang mengisi saluran tidak dibenamkan dalam Goroutine?

Mengapakah kebuntuan berlaku apabila panggilan fungsi yang mengisi saluran tidak dibenamkan dalam Goroutine?

王林
王林ke hadapan
2024-02-10 12:00:10763semak imbas

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

Apabila panggilan fungsi yang mengisi saluran tidak dibenamkan dalam Goroutine, sebab kebuntuan berlaku adalah kerana operasi hantar dan terima saluran disekat. Jika fungsi yang mengisi saluran dipanggil dalam Goroutine utama, dan operasi pengisian tidak dimasukkan ke dalam Goroutine baharu untuk dijalankan di dalam fungsi, maka Goroutine utama akan menunggu saluran mempunyai ruang yang cukup untuk menerima data, dan operasi mengisi tidak dapat dilakukan, dengan itu Membawa kepada kebuntuan. Oleh itu, untuk mengelakkan kebuntuan, kita perlu menggunakan Goroutine untuk pelaksanaan serentak dalam operasi mengisi saluran bagi memastikan operasi pengisian dan operasi penerimaan dapat dilakukan pada masa yang sama.

Kandungan soalan

Saya tahu tentang pilihan sync 包及其 waitgroup dan saya tidak mahu menggunakannya untuk ujian ini. Saya sedang menguji semafor.

Jadi saya ada:

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{}{}
}

Apa yang saya ingin uji adalah sangat mudah: Saya mempunyai createjobs 函数,其唯一目的是将元素添加到通道,在本例中是一个 int 通道。然后我有一个 worker yang akan mengekstrak objek dari saluran itu dan tidur selama 2 saat sebelum mengekstrak elemen seterusnya.

Kini, terdapat juga fungsi penyegerakan. Satu-satunya tujuan fungsi ini adalah untuk mensimulasikan elemen worker 运行时启动的进程。如果此进程处于活动状态,则在 sync 结束时应停止处理 jobs, itulah sebabnya saya mempunyai dua saluran, satu menunjukkan bahawa proses telah dimulakan dan satu lagi bahawa proses telah tamat.

Saya mendapat ralat berikut semasa menjalankan kod saya:

fatal error: all goroutines are asleep - deadlock!

Jika saya mengubah suai cara createjobs dipanggil, bungkusnya dalam goroutine seperti ini:

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

Kemudian kod saya berjalan dengan betul.

Saya cuma nak faham kenapa ini berlaku. Apa yang saya maksudkan ialah: rutin main sedang dilaksanakan dan kemudian ia memanggil main 例程正在执行,然后它调用 createjobs (无换行),因此 main 例程应该被阻止,直到此调用结束。一旦 createjobs 结束,就说明通道中有元素了。 main 继续执行并启动其他 goroutine workersync 来完成它们的工作。在 main (tanpa baris baharu), jadi rutin main harus disekat sehingga panggilan ini tamat. Sebaik sahaja

tamat, bermakna ada elemen dalam saluran. utama meneruskan pelaksanaan dan memulakan gorout lain pekerja dan sync untuk menyelesaikan kerja mereka. Sebelum utama tamat, saya hanya menambah sleeper untuk memberi masa goroutine yang dibuat sebelum ini untuk disiapkan.

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

解决方法

您将 jobs 声明为无缓冲通道,然后尝试将 20 个值同步推入其中。当您调用 createjobs(jobs)Saya tidak meminta penyelesaian lain untuk masalah ini, saya cuma ingin tahu apa yang berlaku apabila

berlaku di luar goroutine.

Penyelesaian

Anda mengisytiharkan pekerjaan sebagai saluran yang tidak ditimbal dan kemudian cuba menolak 20 nilai ke dalamnya secara serentak. Ini akan menyekat fungsi utama anda apabila anda memanggil

. Tukar baris 13 kepada:

    jobs := make(chan int, 20)

...akan menyelesaikan kebuntuan.

EDIT - Penjelasan diminta dalam ulasan:

createjobs(jobs)

Saluran tidak buffer tidak mempunyai kapasiti dan menyekat pelaksanaan pengeluar sehingga pengguna menerima mesej.

Satu analogi yang baik untuk saluran yang tidak dibuffer ialah paip, dalam kes ini prosesnya kelihatan seperti ini:

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

Kebuntuan berlaku kerana

dipanggil serentak dan belum ada pengguna yang sedang berjalan. main()

Ia berfungsi apabila memanggil fungsi (🎜pengeluar🎜) dalam goroutine kerana pada asasnya memasukkan saluran dan membaca saluran berlaku secara selari? 🎜 🎜Ya. Jika pengeluar dipanggil secara tidak segerak, ia tidak akan menyekat fungsi 🎜, jadi pengguna juga berpeluang untuk dipanggil. Dalam kes ini, pengeluar akan menolak semua tugasnya satu demi satu, sama seperti pekerja memakannya satu demi satu. 🎜

Atas ialah kandungan terperinci Mengapakah kebuntuan berlaku apabila panggilan fungsi yang mengisi saluran tidak dibenamkan dalam Goroutine?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam