亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

同時,如何管理值/狀態并避免競爭條件

同時,如何管理值/狀態并避免競爭條件

Go
慕標琳琳 2022-03-03 15:57:47
如何根據進程啟動后發生的事件/條件正確設置/修改值,同時在不創建競爭條件的情況下處理Goroutine 。例如,以下“有效(錯誤)”,輸出為:ping, foo=trueping, foo=falseping, foo=trueping, foo=trueping, foo=truehttps://play.golang.org/p/Y3FafF-nBcpackage mainimport "fmt"type test struct {    ch  chan string    foo bool}func (t *test) run() {    for {        select {        case v := <-t.ch:            fmt.Printf("%+v, foo=%+v\n", v, t.foo)            t.foo = false        default:        }    }}func (t *test) Ping() {    t.ch <- "ping"}func New() *test {    t := &test{        ch: make(chan string),    }    go t.run()    return t}func main() {    t := New()    for i := 0; i <= 10; i++ {        if t.foo {            t.Ping()        }        if i%3 == 0 {            t.foo = true        }    }}但是,如果使用-race選項編譯或運行,我會得到以下輸出:$ go run -race main.goping, foo=true==================WARNING: DATA RACEWrite at 0x00c4200761b8 by goroutine 6:  main.(*test).run()      /main.go:16 +0x1fbPrevious read at 0x00c4200761b8 by main goroutine:  main.main()      /main.go:37 +0x5eGoroutine 6 (running) created at:  main.New()      /main.go:30 +0xd0  main.main()      /main.go:35 +0x33==================ping, foo=falseping, foo=trueping, foo=trueping, foo=trueFound 1 data race(s)exit status 66因此,我想知道我可以使用什么并發模式來更改foogorutine 外部和 gorutine 內部的值,而不會產生競爭條件。
查看完整描述

2 回答

?
子衿沉夜

TA貢獻1828條經驗 獲得超3個贊

你有一些選擇:

  • 使用atomic.Value:樣品 (1)

  • 使用sync.RWMutex:樣品 (3)

  • 使用sync/atomic:樣品 (6)

  • 僅使用通道和 goroutine:示例 (7)

另請參閱: 使用 sync.Mutex 還是通道?


1-您可以使用atomic.Value

值提供了一致類型值的原子加載和存儲。值可以作為其他數據結構的一部分創建。Value 的零值從 Load 返回 nil。調用 Store 后,不得復制 Value。

首次使用后不得復制值。

像這個工作樣本:

// to test the panic use go build -race

package main


import (

    "fmt"

    "sync/atomic"

)


type test struct {

    ch chan string

    atomic.Value

}


func (t *test) run() {

    for {

        select {

        case v := <-t.ch:

            fmt.Printf("%+v, foo=%+v\n", v, t.Load())

            t.Store(false)

        default:

        }

    }

}


func (self *test) Ping() {

    self.ch <- "ping"

}


func New() *test {

    t := &test{

        ch: make(chan string),

    }

    t.Store(false)

    go t.run()

    return t

}


func main() {

    t := New()

    for i := 0; i <= 10; i++ {

        if x, _ := t.Load().(bool); x {

            t.Ping()

        }

        //  time.Sleep(time.Second)

        if i%3 == 0 {

            t.Store(true)

        }

    }

}

輸出go build -race:


ping, foo=true

ping, foo=false

ping, foo=false

ping, foo=false

ping, foo=false

2- 一點改進func (t *test) run():


func (t *test) run() {

    for v := range t.ch {

        fmt.Printf("%+v, foo=%+v\n", v, t.Load())

        t.Store(false)

    }

}

3-您可以使用sync.RWMutexand sync.WaitGroup,就像這個工作示例一樣:


// to test the panic use go build -race

package main


import (

    "fmt"

    "sync"

)


type test struct {

    ch  chan string

    foo bool

    sync.RWMutex

    sync.WaitGroup

}


func (t *test) run() {

    for v := range t.ch {

        t.Lock()

        r := t.foo

        t.foo = false

        t.Unlock()

        fmt.Printf("%+v, foo=%+v\n", v, r)


    }

    t.Done()

}


func (self *test) Ping() {

    self.ch <- "ping"

}


func New() *test {

    t := &test{ch: make(chan string)}

    t.Add(1)

    go t.run()

    return t

}


func main() {

    t := New()

    for i := 0; i <= 10; i++ {

        t.RLock()

        r := t.foo

        t.RUnlock()

        if r {

            t.Ping()

        }

        //  time.Sleep(time.Second)

        if i%3 == 0 {

            t.Lock()

            t.foo = true

            t.Unlock()

        }

    }

    close(t.ch)

    t.Wait()

}

輸出go build -race:


ping, foo=true

ping, foo=true

ping, foo=false

ping, foo=true

ping, foo=false

ping, foo=true

4-所以讓我們按照這種方法https://talks.golang.org/2013/bestpractices.slide#29:

原始代碼:


package main


import (

    "fmt"

    "time"

)


type Server struct{ quit chan bool }


func NewServer() *Server {

    s := &Server{make(chan bool)}

    go s.run()

    return s

}


func (s *Server) run() {

    for {

        select {

        case <-s.quit:

            fmt.Println("finishing task")

            time.Sleep(time.Second)

            fmt.Println("task done")

            s.quit <- true

            return

        case <-time.After(time.Second):

            fmt.Println("running task")

        }

    }

}

func (s *Server) Stop() {

    fmt.Println("server stopping")

    s.quit <- true

    <-s.quit

    fmt.Println("server stopped")

}


func main() {

    s := NewServer()

    time.Sleep(2 * time.Second)

    s.Stop()

}

5-讓我們簡化它:


package main


import (

    "fmt"

    "time"

)


var quit = make(chan bool)


func main() {

    go run()

    time.Sleep(2 * time.Second)

    fmt.Println("server stopping")


    quit <- true // signal to quit


    <-quit // wait for quit signal


    fmt.Println("server stopped")

}


func run() {

    for {

        select {

        case <-quit:

            fmt.Println("finishing task")

            time.Sleep(time.Second)

            fmt.Println("task done")

            quit <- true

            return

        case <-time.After(time.Second):

            fmt.Println("running task")

        }

    }

}

輸出:


running task

running task

server stopping

finishing task

task done

server stopped

6-您的樣本的簡化版本:


// to test the panic use go build -race

package main


import "fmt"

import "sync/atomic"


var ch = make(chan string)

var state int32


func main() {

    go run()

    for i := 0; i <= 10; i++ {

        if atomic.LoadInt32(&state) == 1 {

            ch <- "ping"

        }

        if i%3 == 0 {

            atomic.StoreInt32(&state, 1)

        }

    }

}


func run() {

    for v := range ch {

        fmt.Printf("%+v, state=%+v\n", v, atomic.LoadInt32(&state))

        atomic.StoreInt32(&state, 0)

    }

}

輸出:


ping, state=1

ping, state=0

ping, state=1

ping, state=0

ping, state=1

ping, state=0

7- 帶通道但不使用Lock()(The Go Playground)的工作樣本:


// to test the panic use go build -race

package main


import "fmt"


func main() {

    go run()

    for i := 0; i <= 10; i++ {

        signal <- struct{}{}

        if <-read {

            ping <- "ping"

        }

        if i%3 == 0 {

            write <- true

        }

    }

}


func run() {

    foo := false

    for {

        select {

        case <-signal:

            fmt.Println("signal", foo)

            read <- foo

        case foo = <-write:

            fmt.Println("write", foo)

        case v := <-ping:

            fmt.Println(v, foo)

            foo = false

        }

    }

}


var (

    ping   = make(chan string)

    signal = make(chan struct{})

    read   = make(chan bool)

    write  = make(chan bool)

)

輸出:


signal false

write true

signal true

ping true

signal false

signal false

write true

signal true

ping true

signal false

signal false

write true

signal true

ping true

signal false

signal false

write true

signal true

ping true


查看完整回答
反對 回復 2022-03-03
?
幕布斯6054654

TA貢獻1876條經驗 獲得超7個贊

使用互斥鎖

package main


import (

    "sync"

    "time"

    "fmt"

)


var myvar int

var mut sync.Mutex


func main() {

    for {

        go other()

        go printer()

        time.Sleep(time.Duration(1) * time.Second)

    }

}


func other() {

    mut.Lock()

    myvar = myvar +1

    mut.Unlock()

}


func printer() {

    mut.Lock()

    fmt.Println(myvar)

    mut.Unlock()

}

運行(使用互斥鎖)

$ go build -race t1.go 

$ ./t1 

1

2

3

4

5

6

7

7

9

10

運行(沒有互斥體)

$ go build t2.go 

$ go build -race t2.go 

$ ./t2 

==================

WARNING: DATA RACE

Read at 0x000000580ce8 by goroutine 7:

  runtime.convT2E()

      /usr/local/go/src/runtime/iface.go:155 +0x0

  main.printer()

      /.../.../.../GOPATH/t2.go:23 +0x65


Previous write at 0x000000580ce8 by goroutine 6:

  main.other()

      /.../.../.../GOPATH/t2.go:19 +0x3d


Goroutine 7 (running) created at:

  main.main()

      /.../.../.../GOPATH/t2.go:13 +0x5a


Goroutine 6 (finished) created at:

  main.main()

      /.../.../.../GOPATH/t2.go:12 +0x42

==================

1

2


查看完整回答
反對 回復 2022-03-03
  • 2 回答
  • 0 關注
  • 169 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號