如果不使用mutext并发进行读取或写入,会发生什么?>

问题描述 投票:-3回答:3

在Go中,sync.Mutextchan用于防止并发访问共享对象。但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。还是我喜欢写一个值,不在乎另一个go例程以后是否覆盖它,还是以前才覆盖它。

更新:

TLDR;只是不要这样做。这不安全。阅读答案,评论和链接的文档!

这里是示例程序的两个变体goodbad,它们似乎都使用当前的Go运行时产生“正确的”输出:

package main

import (
    "flag"
    "fmt"
    "math/rand"
    "time"
)

var bogus = flag.Bool("bogus", false, "use bogus code")

func pause() {
    time.Sleep(time.Duration(rand.Uint32()%100) * time.Millisecond)
}

func bad() {
    stop := time.After(100 * time.Millisecond)
    var name string

    // start some producers doing concurrent writes (DANGER!)
    for i := 0; i < 10; i++ {
        go func(i int) {
            pause()
            name = fmt.Sprintf("name = %d", i)
        }(i)
    }

    // start consumer that shows the current value every 10ms
    go func() {
        tick := time.Tick(10 * time.Millisecond)
        for {
            select {
            case <-stop:
                return
            case <-tick:
                fmt.Println("read:", name)
            }
        }
    }()

    <-stop
}

func good() {
    stop := time.After(100 * time.Millisecond)
    names := make(chan string, 10)

    // start some producers concurrently writing to a channel (GOOD!)
    for i := 0; i < 10; i++ {
        go func(i int) {
            pause()
            names <- fmt.Sprintf("name = %d", i)
        }(i)
    }

    // start consumer that shows the current value every 10ms
    go func() {
        tick := time.Tick(10 * time.Millisecond)
        var name string
        for {
            select {
            case name = <-names:
            case <-stop:
                return
            case <-tick:
                fmt.Println("read:", name)
            }
        }
    }()

    <-stop
}

func main() {
    flag.Parse()
    if *bogus {
        bad()
    } else {
        good()
    }
}

预期输出如下:

...
read: name = 3
read: name = 3
read: name = 5
read: name = 4
...

read:read: name=[0-9]的任何组合是此程序的正确输出。接收任何其他字符串作为输出将是错误。

[使用go run --race bogus.go运行该程序是安全的。

但是,go run --race bogus.go -bogus会警告并发读写。

对于map类型,并且在附加到切片时,我始终需要使用互斥锁或类似的保护方法,以避免出现段错误或意外行为。但是,为了安全起见,将字面量(原子值)读写到变量或字段值似乎

问题:

我可以安全地读取和安全地同时写入哪些Go数据类型,而无需使用mutext,不产生段错误以及不从内存中读取垃圾?

解释

为什么答案中的内容[。

Update

:我重写了该示例以更好地反映原始代码,其中我遇到了并发写入问题。重要的观点已经在评论中。我将接受一个答案,该答案以足够详细的方式总结了这些学习内容(尤其是在Go运行时中)。
在Go中,使用sync.Mutext或chan阻止并发访问共享对象。但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。还是我喜欢...
go concurrency mutex shared-memory literals
3个回答
1
投票
但是,在某些情况下,我只是对变量或对象字段的最新值感兴趣。

3
投票
(顺便说一句,您的“正确”程序不正确,它很讲究,即使您摆脱了竞争条件,它也不会确定地产生输出。)

您为什么不能使用频道

package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup // wait group to close channel var buffer int = 1 // buffer of the channel // channel to get the share data cName := make(chan string, buffer) for i := 0; i < 10; i++ { wg.Add(1) // add to wait group go func(i int) { cName <- fmt.Sprintf("name = %d", i) wg.Done() // decrease wait group. }(i) } go func() { wg.Wait() // wait of wait group to be 0 close(cName) // close the channel }() // process all the data for n := range cName { println("read:", n) } }


© www.soinside.com 2019 - 2024. All rights reserved.