Go语言 什么时候应该在通道上使用互斥体?

m4pnthwp  于 12个月前  发布在  Go
关注(0)|答案(2)|浏览(115)

在过去的几周里,我一直在思考一个(不那么简单的)问题:

什么时候使用sync.Mutex最好,反之,什么时候使用chan最好?

看起来,对于很多问题,两种策略都是可以互换的- * 而这正是问题所在!*
使用Golang文档中的this video。下面,我冒昧地在playground中口述了代码,并将其转换为sync.Mutex的等价物。

在真实的世界中遇到的某个问题是否值得使用另一个?

备注:

  • 我是chan的超级粉丝,很难想出一个更优雅的sync. Mutex实现。
  • 值得注意的是,chan实现在同一时间内做的工作更多(达到12)*

Playground:

使用chan执行Ping/pong操作:

package main

import (
    "fmt"
    "time"
)

type Ball struct { hits int }

func main() {
    table := make(chan *Ball)
    go player("ping", table)
    go player("pong", table)

    table <- new(Ball)
    time.Sleep(1 * time.Second)
    <-table
}

func player(name string, table chan *Ball) {
    for {
        ball := <-table
        ball.hits++
        fmt.Println(name, ball.hits)
        time.Sleep(100 * time.Millisecond)
        table <- ball
    }
}

使用sync.Mutex执行Ping/pong操作:

package main

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

type Ball struct { hits int }

var m =  sync.Mutex{}

func main() {
    ball := new(Ball)
    go player("ping", ball)
    go player("pong", ball)

    time.Sleep(1 * time.Second)
}

func player(name string, ball *Ball) {
    for {
        m.Lock()
        ball.hits++
        fmt.Println(name, ball.hits)
        time.Sleep(100 * time.Millisecond)
        m.Unlock()

    }
}
jecbmhm3

jecbmhm31#

在Go语言中,通道是非常棒的,你可以使用它们在goroutines之间进行通信。但是,为了方便起见,您可能希望在某些情况下使用sync.Mutex。这些情况如下:

  • 保卫一个内部国家
  • 缓存问题
  • 以获得更好的性能

这里有三个examples and explanations
1.简单计数器

1.乒乓球比赛

1.最简单的缓存

1sbrub3j

1sbrub3j2#

一些示例信道用例:

*限制并发任务数(例如运行goroutine的数量)使用缓冲的通道容量(和/或长度)
*转移一个对象的所有权(一个且只有一个goroutine写它,而没有其他goroutine阅读它)
*协调、同步、发送信号、数据。

一些示例基元用例:

*保护结构体的内部状态(例如sync.Mutexsync.RWMutex
*性能关键(依赖于算法和用例,不是一般规则)

示例

为了让它更清楚,假设我们需要一个一秒计数器,所以在下面的例子中,我们计数一秒,然后打印计数器值,看看它计数的速度有多快:

No |         Count |       Method
------------------------------------------------------
 1 |     17_729_027 | Using sync.RWMutex for increment   
 2 |     12_180_741 | Using channel for increment    
 3 |    106_743_095 | Using channel for timer 
 4 |    104_178_671 | Using time.AfterFunc and channel sync

注意:go版本go1.13.5 Linux/amd 64
代码:
1 -使用sync.RWMutex进行增量:

package main

import (
    "sync"
    "time"
)

func main() {
    var i rwm
    go func() {
        for {
            i.inc() // free running counter
        }
    }()
    time.Sleep(1 * time.Second)
    println(i.read()) // sampling the counter
}

type rwm struct {
    sync.RWMutex
    i int
}

func (l *rwm) inc() {
    l.Lock()
    defer l.Unlock()
    l.i++
}
func (l *rwm) read() int {
    l.RLock()
    defer l.RUnlock()
    return l.i
}

2 -使用通道进行增量:

package main

import (
    "time"
)

func main() {
    ch := make(chan int, 1)
    ch <- 1
    timeout := time.NewTimer(1 * time.Second)
loop:
    for {
        select {
        case <-timeout.C:
            timeout.Stop()
            break loop
        default:
            ch <- 1 + <-ch
        }
    }

    println(<-ch)
}

3 -使用定时器的通道:

package main

import "time"

func main() {
    ch := make(chan int)
    go func() {
        timeout := time.NewTimer(1 * time.Second)
        defer timeout.Stop()
        i := 1
        for {
            select {
            case <-timeout.C:
                ch <- i
                return
            default:
                i++
            }
        }
    }()

    println(<-ch)
}

4 -使用time.AfterFunc和通道同步:

package main

import (
    "fmt"
    "time"
)

func main() {
    d := 1 * time.Second
    i := uint64(0)
    ch := make(chan struct{})

    time.AfterFunc(d, func() {
        close(ch)
    })

loop:
    for {
        select {
        case <-ch:
            break loop
        default:
            i++
        }
    }

    fmt.Println(i) // 104_178_671
}

相关问题