Blocking a move to teach you to read and write Golang channel

Whether unbuffered channel, or a buffer channel, the situation is blocked there, teach you a trick no longer encounter problems blocking the channel.

This article will explain what the situation there will be blocked, and how to select solve congestion.

Blocking scene

Blocking a total of four scenes, each with a cache and two unbuffered.

Unbuffered channel characteristic that needs to be transmitted after the data read is completed before transmission, it blocks the scene:

  1. No data channel, but the execution of the read channel.
  2. No data channel, the write data path, without reading coroutine.
// 场景1
func ReadNoDataFromNoBufCh() {
    noBufCh := make(chan int)

    <-noBufCh
    fmt.Println("read from no buffer channel success")

    // Output:
    // fatal error: all goroutines are asleep - deadlock!
}

// 场景2
func WriteNoBufCh() {
    ch := make(chan int)

    ch <- 1
    fmt.Println("write success no block")
    
    // Output:
    // fatal error: all goroutines are asleep - deadlock!
}

Note: the results of the sample code Output comment function on behalf of each function are due to the obstruction can not continue down the channel operation, the final report of the deadlock error.

There caching channel is characterized by, when the data is directly returned to the channel can be written in the cache, the data can be read from the channel buffer when the data is directly returned, then there is not caching channel blocking, blocking it The scene is:

  1. No data caching channel, but the execution of the read channel.
  2. Caching channel has been filled, write data to the channel, but no coroutine read.
// 场景1
func ReadNoDataFromBufCh() {
    bufCh := make(chan int, 1)

    <-bufCh
    fmt.Println("read from no buffer channel success")

    // Output:
    // fatal error: all goroutines are asleep - deadlock!
}

// 场景2
func WriteBufChButFull() {
    ch := make(chan int, 1)
    // make ch full
    ch <- 100

    ch <- 1
    fmt.Println("write success no block")
    
    // Output:
    // fatal error: all goroutines are asleep - deadlock!
}

Select Use non-blocking read and write

select a configuration selection operation is performed, it contains a set of case statements, it will execute that one of them non-blocking, if all blocked, then wait one not blocked, and then continue, it has a default statement, the statement is never blocked, we can use it to achieve non-blocking operation.

The following sample code is read without using the buffer channel select and modify buffer channels, the following functions can be called directly from the main function, which are the results Ouput comments, it can be seen from the results, in the channel or not unreadable write, wait no longer blocked, but directly returned.

// 无缓冲通道读
func ReadNoDataFromNoBufChWithSelect() {
    bufCh := make(chan int)

    if v, err := ReadWithSelect(bufCh); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("read: %d\n", v)
    }

    // Output:
    // channel has no data
}

// 有缓冲通道读
func ReadNoDataFromBufChWithSelect() {
    bufCh := make(chan int, 1)

    if v, err := ReadWithSelect(bufCh); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("read: %d\n", v)
    }

    // Output:
    // channel has no data
}

// select结构实现通道读
func ReadWithSelect(ch chan int) (x int, err error) {
    select {
    case x = <-ch:
        return x, nil
    default:
        return 0, errors.New("channel has no data")
    }
}

// 无缓冲通道写
func WriteNoBufChWithSelect() {
    ch := make(chan int)
    if err := WriteChWithSelect(ch); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("write success")
    }

    // Output:
    // channel blocked, can not write
}

// 有缓冲通道写
func WriteBufChButFullWithSelect() {
    ch := make(chan int, 1)
    // make ch full
    ch <- 100
    if err := WriteChWithSelect(ch); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("write success")
    }

    // Output:
    // channel blocked, can not write
}

// select结构实现通道写
func WriteChWithSelect(ch chan int) error {
    select {
    case ch <- 1:
        return nil
    default:
        return errors.New("channel blocked, can not write")
    }
}

Select + using improved non-blocking read and write timeout

Use non-blocking channel blocking default realize there is a flaw : When the channel unreadable or written, will to return . The actual scene, more demand, we hope, will attempt to read a data, data or try to write a will, if it can not read and write, and then return the program to continue to do other things.

Using the timer alternative default can solve this problem. For example, I read and write data to tolerate the passage of time is 500ms, if still can not read and write, they return immediately, change it would be like this:

func ReadWithSelect(ch chan int) (x int, err error) {
    timeout := time.NewTimer(time.Microsecond * 500)

    select {
    case x = <-ch:
        return x, nil
    case <-timeout.C:
        return 0, errors.New("read time out")
    }
}

func WriteChWithSelect(ch chan int) error {
    timeout := time.NewTimer(time.Microsecond * 500)

    select {
    case ch <- 1:
        return nil
    case <-timeout.C:
        return errors.New("write time out")
    }
}

The results will become a time-out return:

read time out
write time out
read time out
write time out

If this article help you, please point a praise / like it, let me know my writing is valuable, thanks.



Author: Dabin _ learn together Golang
link: https: //www.jianshu.com/p/3b24e909905f
Source: Jane book
Jane book copyright reserved by the authors, are reproduced in any form, please contact the author to obtain authorization and indicate the source.

Guess you like

Origin blog.csdn.net/runner668/article/details/90573985