Golang中如何从goroutine调用方法
Golang中如何从goroutine调用方法 我有一个对象 A,它有一些方法:Get1()、Get2()、Get3()…
该对象正在被一个 goroutine 使用。
另一个 goroutine 想要调用这些 Get 方法。
应该如何安全地实现这一点?
我能想到几种处理方式。 可以添加一个互斥锁,但指导原则似乎是在可能的情况下使用通道。
我的问题是:在 Go 中,最符合语言习惯的安全实现方式是什么?
谢谢, Mike
当从多个 goroutine 调用访问共享状态(如全局变量或可变数据结构)的方法时,请务必小心。这可能导致竞态条件,即结果取决于 goroutine 不可预测的调度。请使用互斥锁或通道等同步机制来确保对共享数据的安全访问。
更多关于Golang中如何从goroutine调用方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您的建议!
我原以为可能会用到通道。
我想闭包可以被放入通道,以延迟对 GetA 的调用。
但这会增加许多复杂性。
您使用简单互斥锁的例子似乎是更好的方法。
你能做类似这样的事情吗?
func main() {
a := Obj.New()
go func(b){
b.Get1()
}(&a)
go func(c){
c.Get2()
}(&a)
...
}
在我看来,地道的做法是在结构体中添加互斥锁。我假设由于这是 get 方法,你只是读取数据,而不修改它。所以可以像这样做:
package main
type A struct {
sync.RWMutex
data string
}
func (a *A) Get() string {
a.RLock()
defer a.RUnlock()
return a.data
}
func main() {
a := &A{data: "some string"}
go func() {
fmt.Println(a.Get()) // Prints "some string"
}()
// Some code here, to let goroutine to finish...
}
在Go中,从另一个goroutine安全调用对象方法,最符合语言习惯的方式是使用通道进行通信。以下是几种实现方式:
1. 使用通道传递函数调用
type A struct {
requests chan func()
}
func NewA() *A {
a := &A{
requests: make(chan func()),
}
go a.run()
return a
}
func (a *A) run() {
for f := range a.requests {
f()
}
}
func (a *A) Get1() int {
result := make(chan int)
a.requests <- func() {
// 实际执行Get1的逻辑
result <- 42
}
return <-result
}
func (a *A) Get2() string {
result := make(chan string)
a.requests <- func() {
// 实际执行Get2的逻辑
result <- "value"
}
return <-result
}
2. 使用请求-响应模式
type Request struct {
action string
data interface{}
resp chan interface{}
}
type A struct {
reqChan chan Request
}
func NewA() *A {
a := &A{
reqChan: make(chan Request),
}
go a.processor()
return a
}
func (a *A) processor() {
for req := range a.reqChan {
switch req.action {
case "Get1":
req.resp <- a.get1Impl()
case "Get2":
req.resp <- a.get2Impl()
}
}
}
func (a *A) Get1() int {
resp := make(chan interface{})
a.reqChan <- Request{
action: "Get1",
resp: resp,
}
return (<-resp).(int)
}
func (a *A) get1Impl() int {
return 42
}
3. 使用互斥锁的简单方案
type A struct {
mu sync.RWMutex
value int
}
func (a *A) Get1() int {
a.mu.RLock()
defer a.mu.RUnlock()
return a.value
}
func (a *A) SetValue(v int) {
a.mu.Lock()
defer a.mu.Unlock()
a.value = v
}
4. 带上下文的通道方案
type A struct {
cmdChan chan command
}
type command struct {
fn func() interface{}
resp chan interface{}
}
func NewA() *A {
a := &A{
cmdChan: make(chan command),
}
go a.loop()
return a
}
func (a *A) loop() {
for cmd := range a.cmdChan {
cmd.resp <- cmd.fn()
}
}
func (a *A) execute(fn func() interface{}) interface{} {
resp := make(chan interface{})
a.cmdChan <- command{fn: fn, resp: resp}
return <-resp
}
func (a *A) Get1() int {
return a.execute(func() interface{} {
return 42
}).(int)
}
通道方案更符合Go的并发哲学:“不要通过共享内存来通信,而应该通过通信来共享内存”。第一种方案是最常见的实现模式,它确保了所有对A的方法调用都在同一个goroutine中顺序执行,完全避免了竞态条件。

