如何优雅的关闭golang的channel

如何优雅的关闭golang的channel,第1张

概述How to Gracefully Close Channels,这篇博客讲了如何优雅的关闭channel的技巧,好好研读,收获良多。 众所周知,在golang中,关闭或者向已关闭的channel发送数据都会引发panic。 谨遵优雅关闭channel的原则 不要在接受一端关闭channel 不要在有多个并发的senders中关闭channel。反过来说,如果只有一个协程充当sender,那么我们 @H_404_1@

How to Gracefully Close Channels,这篇博客讲了如何优雅的关闭channel的技巧,好好研读,收获良多。

众所周知,在golang中,关闭或者向已关闭的channel发送数据都会引发panic。

谨遵优雅关闭channel的原则 不要在接受一端关闭channel 不要在有多个并发的senders中关闭channel。反过来说,如果只有一个协程充当sender,那么我们可以在这个sender协程内关闭掉channel。 一个简单的方法 SafeClose
type MyChannel struct {    C      chan T    closed bool    mutex  sync.Mutex}func NewMyChannel() *MyChannel {    return &MyChannel{C: make(chan T)}}func (mc *MyChannel) SafeClose() {    mc.mutex.Lock()    defer mc.mutex.Unlock()    if !mc.closed {        close(mc.C)        mc.closed = true    }}func (mc *MyChannel) IsClosed() bool {    mc.mutex.Lock()    defer mc.mutex.Unlock()    return mc.closed}
SafeSend
func SafeSend(ch chan T,value T) (closed bool) {    defer func() {        if recover() != nil {            closed = true        }    }()    ch <- value  // panic if ch is closed    return false // <=> closed = false; return}

[x] 那边英文博客有一句话

@H_404_36@

One drawback of the above SafeSend function is that its calls can‘t be used as send operations which follow the case keyword in select blocks.

这里指的是SafeSend方法不能用在select...case...的case接受 *** 作中,即

select {    case <- SafeSend(ch,1)}

因为case后面需要一个channel。

优雅关闭channel的设计 多个receivers,一个sender的情况。
package mainimport (    "time"    "math/rand"    "sync"    "log")func main() {    rand.Seed(time.Now().UnixNano())    log.SetFlags(0)    // ...    const MaxRandomNumber = 100000    const NumReceivers = 100    wgReceivers := sync.WaitGroup{}    wgReceivers.Add(NumReceivers)    // ...    dataCh := make(chan int,100)    // the sender    go func() {        for {            if value := rand.Intn(MaxRandomNumber); value == 0 {                // The only sender can close the channel safely.                close(dataCh)                return            } else {                dataCh <- value            }        }    }()    // receivers    for i := 0; i < NumReceivers; i++ {        go func() {            defer wgReceivers.Done()            // Receive values until dataCh is closed and            // the value buffer queue of dataCh is empty.            for value := range dataCh {                log.Println(value)            }        }()    }    wgReceivers.Wait()}
一个receiver,多个senders的情况。
package mainimport (    "time"    "math/rand"    "sync"    "log")func main() {    rand.Seed(time.Now().UnixNano())    log.SetFlags(0)    // ...    const MaxRandomNumber = 100000    const NumSenders = 1000    wgReceivers := sync.WaitGroup{}    wgReceivers.Add(1)    // ...    dataCh := make(chan int,100)    stopCh := make(chan struct{})        // stopCh is an additional signal channel.        // Its sender is the receiver of channel dataCh.        // Its receivers are the senders of channel dataCh.    // senders    for i := 0; i < NumSenders; i++ {        go func() {            for {                // The try-receive operation is to try to exit                // the goroutine as early as possible. For this                // specifIEd example,it is not essential.                select {                case <- stopCh:                    return                default:                }                // Even if stopCh is closed,the first branch in the                // second select may be still not selected for some                // loops if the send to dataCh is also unblocked.                // But this is acceptable for this example,so the                // first select block above can be omitted.                select {                case <- stopCh:                    return                case dataCh <- rand.Intn(MaxRandomNumber):                }            }        }()    }    // the receiver    go func() {        defer wgReceivers.Done()        for value := range dataCh {            if value == MaxRandomNumber-1 {                // The receiver of the dataCh channel is                // also the sender of the stopCh channel.                // It is safe to close the stop channel here.                close(stopCh)                return            }            log.Println(value)        }    }()    // ...    wgReceivers.Wait()}
多个receivers和多个senders
package mainimport (    "time"    "math/rand"    "sync"    "log"    "strconv")func main() {    rand.Seed(time.Now().UnixNano())    log.SetFlags(0)    // ...    const MaxRandomNumber = 100000    const NumReceivers = 10    const NumSenders = 1000    wgReceivers := sync.WaitGroup{}    wgReceivers.Add(NumReceivers)    // ...    dataCh := make(chan int,100)    stopCh := make(chan struct{})        // stopCh is an additional signal channel.        // Its sender is the moderator goroutine shown below.        // Its receivers are all senders and receivers of dataCh.    toStop := make(chan string,1)        // The channel toStop is used to notify the moderator        // to close the additional signal channel (stopCh).        // Its senders are any senders and receivers of dataCh.        // Its receiver is the moderator goroutine shown below.        // It must be a buffered channel.    var stoppedBy string    // moderator    go func() {        stoppedBy = <-toStop        close(stopCh)    }()    // senders    for i := 0; i < NumSenders; i++ {        go func(ID string) {            for {                value := rand.Intn(MaxRandomNumber)                if value == 0 {                    // Here,the try-send operation is to notify the                    // moderator to close the additional signal channel.                    select {                    case toStop <- "sender#" + ID:                    default:                    }                    return                }                // The try-receive operation here is to try to exit the                // sender goroutine as early as possible. Try-receive                // try-send select blocks are specially optimized by the                // standard Go compiler,so they are very efficIEnt.                select {                case <- stopCh:                    return                default:                }                // Even if stopCh is closed,the first branch in this                // select block may be still not selected for some                // loops (and for ever in theory) if the send to dataCh                // is also non-blocking. If this is not acceptable,// then the above try-receive operation is essential.                select {                case <- stopCh:                    return                case dataCh <- value:                }            }        }(strconv.Itoa(i))    }    // receivers    for i := 0; i < NumReceivers; i++ {        go func(ID string) {            defer wgReceivers.Done()            for {                // Same as the sender goroutine,the try-receive                // operation here is to try to exit the receiver                // goroutine as early as possible.                select {                case <- stopCh:                    return                default:                }                // Even if stopCh is closed,the first branch in this                // select block may be still not selected for some                // loops (and for ever in theory) if the receive from                // dataCh is also non-blocking. If this is not acceptable,// then the above try-receive operation is essential.                select {                case <- stopCh:                    return                case value := <-dataCh:                    if value == MaxRandomNumber-1 {                        // The same trick is used to notify                        // the moderator to close the                        // additional signal channel.                        select {                        case toStop <- "receiver#" + ID:                        default:                        }                        return                    }                    log.Println(value)                }            }        }(strconv.Itoa(i))    }    // ...    wgReceivers.Wait()    log.Println("stopped by",stoppedBy)}
总结

以上是内存溢出为你收集整理的如何优雅的关闭golang的channel全部内容,希望文章能够帮你解决如何优雅的关闭golang的channel所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1268371.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-08
下一篇2022-06-08

发表评论

登录后才能评论

评论列表(0条)

    保存