Go 转到对指针方法的并发访问

Go 转到对指针方法的并发访问,go,goroutine,Go,Goroutine,我试图理解当您并发访问指针方法时会发生什么 我有一个指针地图,并衍生出一些围棋例程。我将映射传递到每个go例程中,每个go例程将使用映射中的一个值。没有任何东西被写入地图,只有从中读取 地图很小,只有4个键,因此可能有多个围棋程序使用地图中的相同值 问题是,当两个go例程调用同一指针的方法时会发生什么?我会得到不可预测的结果吗 编辑 示例:我把地图部分拿出来,因为这不是我要问的问题 我有foo这是一个类型为MyStruct的指针,这个结构有一个接受参数的方法DoSomething。在main函数

我试图理解当您并发访问指针方法时会发生什么

我有一个指针地图,并衍生出一些围棋例程。我将映射传递到每个go例程中,每个go例程将使用映射中的一个值。没有任何东西被写入地图,只有从中读取

地图很小,只有4个键,因此可能有多个围棋程序使用地图中的相同值

问题是,当两个go例程调用同一指针的方法时会发生什么?我会得到不可预测的结果吗

编辑

示例:我把地图部分拿出来,因为这不是我要问的问题

我有
foo
这是一个类型为
MyStruct
的指针,这个结构有一个接受参数的方法
DoSomething
。在
main
函数中,我创建了两个
go例程
,它们都调用
foo.DoSomething
传递不同的值。在本例中,第一个go例程的计算量要比第二个例程大得多(这里仅使用睡眠时间来模拟计算)。同样,结构中没有任何变化,我只是调用structures方法。当第一个go例程仍在使用该方法时,我是否需要担心第二个go例程调用
foo.DoSomething

package main

import (
    "log"
    "time"
)

type MyStruct struct {
}

func (self *MyStruct) DoSomething(value int) {

    log.Printf("%d Start", value)

    calculation_time := time.Duration(value) * time.Second
    log.Printf("%d Calculating", value, calculation_time)
    time.Sleep(calculation_time)

    log.Printf("%d Done", value)
}

func main() {

    var foo = new(MyStruct)

    go foo.DoSomething(5)

            // is this method call a problem when the first one is still working?
    go foo.DoSomething(2)

    time.Sleep(time.Duration(6 * time.Second))
}

任何指针都被认为不是线程安全的。go例程应该被视为一个单独的线程,即使它可能不是。Go例程通过操作系统线程进行多路复用

如果该值始终为只读(永远不会更改),则可以根据需要读取任意多个go例程。一旦更改该值,就会得到不一致的结果

要同步访问并避免问题(和潜在的恐慌),您必须使用。因此,您可以使用getter和setter函数,而不是直接读/写。getter将使用
m.RLock()
m.RUnlock()
。setter将使用
m.Lock()
m.Unlock()

使用互斥锁时,请尝试尽快解锁。将代码尽可能短地保持在锁定和解锁之间:

m.Lock()
// Do what you need to do for the lock
mymap[key] = value
m.Unlock()
// Do everything else here
与之不同的是,它允许您同时拥有任意数量的读卡器(RLock代表readlock)。一旦写入程序尝试获取锁,它就会阻止其他读卡器获取锁,并等待退出的读卡器释放其锁


或者,您可以使用通道在go例程之间传递值。渠道适用于许多情况,并受到鼓励。您可以在中阅读有关并发性的更多信息。不过,频道并不总是适合所有情况,因此这取决于具体情况。

Go方法有接收器。可以是指针类型。具有签名的方法,例如:

func (r *R) foo(bar baz) // A method
是as吗

换句话说,接收者,不管是否是指针,只是一个参数槽。现在,您的问题归结为:

当两个go例程使用相同的r值调用上述函数时会发生什么情况

A:看情况。问题配置:

  • foo
    不会因为任何原因重新进入
  • foo
    变异
    *r
    (指针对象)不进行协调/同步
  • 最后一点只是一个特例:如果
    foo
    在没有协调/同步的情况下改变任何共享状态:任何事情都可能发生

如果
foo
避免了上述问题,那么即使具有相同的r值,也可以由多个goroutine同时执行。每当有人在其他人读取变量时修改变量时,就会得到一个竞争条件。在您的示例中,变量
foo
/
self
由许多goroutine同时读取,但由于在并发访问时没有人修改它,所以一切都很好

一个更有趣的例子是带有一些附加属性的struct
MyStruct
,例如
attribute1
。在这种情况下,同样的规则也适用于属性。您可以同时从不同的goroutine读取它-例如,您的
DoSomething
方法也可以打印出
self.attribute1
的值-但在此期间不允许修改它

如果希望能够在访问变量时对其进行修改,则需要某种类型的同步原语。惯用的Go方法是,只要您需要来自另一个goroutine的数据,就只使用从单个goroutine访问并通过通道进行通信的局部变量,从而避免同时访问同一个变量


Go中也提供的替代方法是来自
sync
包的原语,如sync.Mutex。
sync/atomic
软件包还提供了更细粒度的原语,可用于以原子方式加载/存储/修改/比较和交换变量。

感谢您花时间帮助我,但这并不是我想要的问题。我添加了一个edit,其中包含一个代码示例,说明了我想要了解的内容。很抱歉给您带来任何困惑。@Jeff,正如我的回答所说,“如果值始终是只读的(永远不会更改),您可以根据需要读取任意多个go例程。”。它不必是特定于地图的-它是一个指针。这同样适用。
func foo(r *R, bar baz) // A plain old function