Rumah >pembangunan bahagian belakang >Golang >Memahami tingkah laku saluran menyekat golang dengan menulis beberapa kali ke saluran

Memahami tingkah laku saluran menyekat golang dengan menulis beberapa kali ke saluran

WBOY
WBOYke hadapan
2024-02-09 14:27:20797semak imbas

通过对通道进行多次写入来理解 golang 阻塞通道行为

Dalam artikel ini, editor PHP Youzi akan memperkenalkan kepada anda cara memahami tingkah laku golang menyekat saluran dengan menulis ke saluran berkali-kali. Dalam golang, saluran adalah mekanisme penting untuk menghantar data antara coroutine. Apabila saluran penuh, operasi tulis disekat sehingga saluran menjadi bebas. Kami akan menunjukkan tingkah laku ini dengan contoh mudah dan menerangkan cara menyekat saluran berfungsi dan cara menggunakannya. Sama ada anda seorang pemula atau pembangun golang yang berpengalaman, anda boleh memperoleh pengetahuan berguna dan pengalaman praktikal daripada artikel ini. Mari mulakan!

Isi soalan

Saya baru mengenali golang dan cuba memahami konkurensi dalam bahasa. Saya mempunyai kod yang menolak beberapa nilai ke saluran dan kemudian membacanya.

package main

import (
    "log"
    "time"
)

func Greet2(c chan string) {
    // logging to Stdout is not an atomic operation
    // so artificially, sleep for some time
    time.Sleep(2 * time.Second)
    
    // 5. until below line reads and unblock the channel
    log.Printf("5. Read Greet2:: %s\n\n", <-c)
}

func Greet(c chan string) {
    // 4. Push a new value to the channel, this will block
    // Process will look for other go routines to execute
    log.Printf("4. Add 'Greet::John' to the channel, block until it is read. Remember, 'Greet' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.\n\n")
    c <- "Greet::John!"
    
    // 8. This statement will never execute
    log.Printf("8. Read Greet:: %s !\n\n", <-c)
}

func main() {

    c := make(chan string)

    log.Println("1. Main start")
    
    // 2. Both go routine will be declared and both will
    // for a value to be inserted in the channel
    log.Println("2. Declare go routines.\n\n")
    go Greet(c)
    go Greet2(c)
    
    // 3. write will block
    log.Println("3. Add 'main::Hello' to the channel, block until it is read. Remember, 'main' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.\n\n")
    c <- "main::Hello"
    
    // Sleep to give time goroutines to execute
    time.Sleep(time.Second)
    
    // 6. read the channel value.
    log.Printf("6. Read main:: %s \n\n", <-c)
    
    // 7. Insert a new value to the channel
    log.Println("7. Add 'main::Bye' to the channel, block until it is read.\n")
    c <- "main::Bye"
    
    // Sleep to give time goroutines to execute
    time.Sleep(time.Second)
    log.Println("9. Main stop")

}

Keluaran program di atas ialah

2023/09/02 21:58:07 1. Main start
2023/09/02 21:58:07 2. Declare go routines.


2023/09/02 21:58:07 3. Add 'main::Hello' to the channel, block until it is read. Remember, 'main' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.


2023/09/02 21:58:07 4. Add 'Greet::John' to the channel, block until it is read. Remember, 'Greet' goroutine will block and only other goroutines can run even though this go routine can pull the value out from the channel.

2023/09/02 21:58:10 5. Read Greet2:: main::Hello

2023/09/02 21:58:11 6. Read main:: Greet::John!

2023/09/02 21:58:11 7. Add 'main::Bye' to the channel, block until it is read.

2023/09/02 21:58:11 8. Read Greet:: main::Bye !

2023/09/02 21:58:12 9. Main stop

Saya tidak faham kenapa 4.(另一个写入通道)在 5.(第一次从通道读取)之前执行,因为 3. 将阻塞,并且在读取值之前通道不可用来自它(在步骤 5. 中)。我是否误解了阻塞行为,在步骤 3. 中,只有 main goroutine 块和 Greet (在步骤 4. ) boleh menulis nilai tambahan pada saluran? Penjelasan benar-benar akan menjelaskan kekeliruan saya :)

Sekian, D.D.

Terima kasih atas balasan anda, saya telah mencipta program yang lebih mudah untuk ditunjukkan. Concurrency

package main

import (
        "fmt"
)

func do2(c chan int) {
        fmt.Println(<-c)
}

func do(c chan int) {
        // 4. this statement is trying to write another value "2" to the channel
        // Channel already contains "1" as the value which has not been read yet.
        // this statement will wait for "1" to get read and block the execution.
        // Scheduler will look for other goroutines that can execute.
        // However, this("do") is blocked as well as "main" is blocked too and
        // there are no other goroutines to execute.
        // Hence, will result in a "Deadlock" fatal error.
        c <- 2
        fmt.Println(<-c)
}

func main() {

        // 1. Declare a channel
        c := make(chan int)
        // 2. Declare "do" goroutine
        go do(c)
        // 3. write "1" to the channel
        // This will block and wait for program's other goroutines to read the value.
        // however, there is only "do" goroutine is defined can run at this point.
        // Scheduler, will try to run "do" goroutine.
        c <- 1
        go do2(c)

}

Kebuntuan boleh dibetulkan dengan menukar penyataan c <- 1 dan go do2(c). 死锁可以通过交换c <- 1go do2(c)语句来修复。

解决方法

在 Go 中,当您在通道上发送值时(步骤 3 中的 c <- "main::Hello"),发送 Goroutine 将阻塞,直到有另一个 Goroutine 准备好从通道接收值。然而,这并不意味着没有其他 goroutine 可以继续执行。在您的代码中, GreetGreet2

Penyelesaian

Dalam Go, apabila anda menghantar nilai pada saluran (c <- "utama::Hello" dalam langkah 3), penghantaran Goroutine akan menyekat sehingga terdapat A Goroutine yang lain bersedia untuk menerima nilai daripada saluran. Walau bagaimanapun, ini tidak bermakna tiada gorout lain boleh terus dilaksanakan. Dalam kod anda,
    coroutine semuanya menunggu nilai daripada saluran, jadi apabila anda menghantar nilai dalam langkah 3, salah satu daripada mereka (tiada jaminan yang mana satu) akan menyahsekat dan meneruskan pelaksanaan.
  1. cIzinkan saya memecahkan urutan peristiwa langkah demi langkah:
  2. GreetGreet2Program utama bermula dan anda mencipta saluran
  3. .
  4. GreetGreet2Anda mengisytiharkan dua gorouti,
  5. , dan kedua-duanya sedang menunggu nilai daripada saluran.
  6. Greet 解除阻塞并继续执行。它记录消息“4. 将‘Greet::John’添加到频道...”并发送“Greet::John!”在频道上。这会再次阻塞 GreetAnda menghantar nilai "utama::Hello" pada saluran, yang menyekat goroutine utama sehingga goroutin lain membaca data daripada saluran. Walau bagaimanapun, salah satu daripada dua Goroutine (
  7. ) tidak akan disekat daripada menerima nilai.
  8. Greet2
  9. , kerana tiada goroutine lain boleh membaca dari saluran pada masa ini.
  10. Nyahsekat dan teruskan pelaksanaan. Ia log mesej "5. Baca Greet2::main::Hello" dan membaca nilai "utama::Hello" daripada saluran. Greet 在向通道写入时仍被阻止,而 Greet2
  11. Utama dibuka, merekodkan "6. Baca utama::Sambut::John!"
  12. GreetMain menghantar nilai lain "utama::Bye" pada saluran. Pada ketika ini,
  13. masih disekat semasa menulis ke saluran, dan
  14. disekat daripada tidak membaca daripada saluran.
Memandangkan

masih disekat pada penulisan, ia tidak pernah mencatatkan "8. Baca Salam:: utama::Selamat tinggal Greet2 碰巧首先被解锁,但也可能是 Greet

Tapak utama.

🎜Jadi kunci untuk memahami gelagat di sini ialah apabila anda menghantar nilai pada saluran, ia membuka kunci mana-mana goroutine yang sedang menunggu untuk membaca data daripada saluran tersebut. Susunan goroutin menunggu dinyahsekat tidak ditentukan dan bergantung pada penjadual. Dalam kes anda, 🎜 kebetulan dibuka kuncinya dahulu, tetapi mungkin juga 🎜. 🎜 🎜Ringkasnya, gelagat yang anda perhatikan adalah konsisten sepenuhnya dengan cara saluran Go berfungsi, dengan kaveat bahawa perintah pelaksanaan antara Goroutines yang bersaing tidak dijamin. 🎜

Atas ialah kandungan terperinci Memahami tingkah laku saluran menyekat golang dengan menulis beberapa kali ke saluran. 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