dougou5852 2017-06-25 10:22
浏览 36

选择中多个事物同时发生时的预期行为

Assuming one goroutine is waiting on the following select on two unbuffered channels one and two

select {
    case <-one:
        fmt.Println("read from one")
    case <-two:
        fmt.Println("read from two")
}

and one one goroutine is waiting on the following send

one <- 1

and another is waiting on the following

two <- 2

The first waiting on a select implies that there is room in the buffer for both the channels one and two, then which select case is guaranteed to run? Is it deterministic or can either run with one channel left with one unread value at the end.

If there is only one guaranteed net output, then do selects ensure a total order across all operations on all the channels participating in the select? That seems very inefficient..


For example in the following code

package main

import (
    "fmt"
    "time"
    "sync"
)

func main() {
    one_net := 0
    two_net := 0
    var mtx = &sync.Mutex{}
    for i := 0; i < 8; i++ {
        one, two := make(chan int), make(chan int)
        go func() { // go routine one
            select {
            case <-one:
                fmt.Println("read from one")

                mtx.Lock()
                one_net++
                mtx.Unlock()
            case <-two:
                fmt.Println("read from two")

                mtx.Lock()
                two_net++
                mtx.Unlock()
            }
        }()
        go func() { // go routine two
            one <- 1

            mtx.Lock()
            one_net--
            mtx.Unlock()

            fmt.Println("Wrote to one")
        }()
        go func() { // go routine three
            two <- 2

            mtx.Lock()
            two_net--
            mtx.Unlock()

            fmt.Println("Wrote to two")
        }()
        time.Sleep(time.Millisecond)
    }
    mtx.Lock()
    fmt.Println("one_net", one_net)
    fmt.Println("two_net", two_net)
    mtx.Unlock()
}

can there even be a mismatch in the number of reads vs the number of writes (i.e. can one_net and two_net be non 0 at the end)? For example in the case where the select statement is waiting on a read from both channels, and then goroutines two and three go through with their respective writes, but then the select only picks up on one of those writes.

  • 写回答

2条回答 默认 最新

  • douhan4093 2017-06-25 10:53
    关注

    The Go Programming Language Specification

    Select statements

    A "select" statement chooses which of a set of possible send or receive operations will proceed.

    If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.

    Your question is imprecise: How to create a Minimal, Complete, and Verifiable example. For example,

    chan.go:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        fmt.Println()
        for i := 0; i < 8; i++ {
            one, two := make(chan int), make(chan int)
            go func() { // goroutine one
                select {
                case <-one:
                    fmt.Println("read from one")
                case <-two:
                    fmt.Println("read from two")
                }
                select {
                case <-one:
                    fmt.Println("read from one")
                case <-two:
                    fmt.Println("read from two")
                }
                fmt.Println()
            }()
            go func() { // goroutine two
                one <- 1
            }()
            go func() { // goroutine three
                two <- 2
            }()
            time.Sleep(time.Millisecond)
        }
    }
    

    Output:

    $ go run chan.go
    
    read from two
    read from one
    
    read from one
    read from two
    
    read from one
    read from two
    
    read from two
    read from one
    
    read from one
    read from two
    
    read from two
    read from one
    
    read from one
    read from two
    
    read from two
    read from one
    
    $
    

    What behavior do you expect and why?


    The Go Programming Language Specification

    Channel types

    A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.

    A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:

    make(chan int, 100)
    

    The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

    Go statements

    A "go" statement starts the execution of a function call as an independent concurrent thread of control, or goroutine, within the same address space.

    The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.

    Analyzing your new example:

    The channels are unbuffered. Goroutines two and three wait on goroutine one. A send on an unbuffered channel waits until there is a pending receive. When the goroutine one select is evaluated, there will be a pending receive on either channel one or channel two. The goroutine, two or three, that sends on that channel can now send and terminate. Goroutine one can now execute a receive on that channel and terminate. As a crude goroutine synchronization mechanism, we wait goroutine main for one millisecond and then terminate it, which terminates any other goroutines. It will terminate the goroutine, two or three, that didn't get to send because it's still waiting for a pending receive.

    You ask "can there even be a mismatch in the number of reads vs the number of writes (i.e. can one_net and two_net be non 0 at the end)? For example in the case where the select statement is waiting on a read from both channels, and then goroutines two and three go through with their respective writes, but then the select only picks up on one of those writes."

    Only one of goroutines two and three gets to send (write). There will be exactly one (send) write and one (receive) read. This assumes that goroutine main does not terminate before this occurs, that is, it occurs within one millisecond.

    评论

报告相同问题?

悬赏问题

  • ¥15 运动想象脑电信号数据集.vhdr
  • ¥15 三因素重复测量数据R语句编写,不存在交互作用
  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目