9

ポインターメソッドに同時アクセスするとどうなるかを理解しようとしていますか?

ポインターのマップがあり、いくつかの go ルーチンを生成します。各 go ルーチンにマップを渡すと、各 go ルーチンはマップ内の値の 1 つを使用します。読み取られるだけのマップには何も書き込まれません。

マップは小さく、4 つのキーしかないため、複数の go ルーチンがマップから同じ値を使用する可能性があります。

質問は、2 つの go ルーチンが同じポインターのメソッドを呼び出すとどうなるかということです。予測できない結果が得られますか?

編集

例: マップの部分を取り出しているのは、それは私が求めている質問ではないからです。

私はfooこれが型のポインタでMyStructあり、この構造にDoSomethingは引数を取るメソッドがあります。main関数で 2 つ作成していますが、両方とも異なる値を渡すgo routines呼び出しを行います。foo.DoSomethingこの例では、最初の go ルーチンは 2 番目のルーチンよりもはるかに大きな計算を実行します (ここではスリープ時間を使用して計算をシミュレートしています)。繰り返しますが、構造体は何も変更されていません。構造体メソッドを呼び出しているだけです。foo.DoSomething最初の go ルーチンがまだメソッドを操作しているときに、2 番目の go ルーチンが を呼び出すことを心配する必要はありますか?

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))
}
4

3 に答える 3

6

ポインターはスレッドセーフではないと見なされます。go ルーチンは、別のスレッドとして扱われるべきです。Go ルーチンは、OS スレッド上で多重化されます。

値が常に読み取り専用 (決して変更されない) の場合、必要な数の go ルーチンから読み取ることができます。値を変更するとすぐに、一貫性のない結果が得られます。

アクセスを同期し、問題 (および潜在的なパニック) を回避するには、sync.RWMutexを使用する必要があります。したがって、直接読み書きする代わりに、getter および setter 関数を使用します。ゲッターは and を使用m.RLock()m.RUnlock()ます。セッターは and を使用m.Lock()m.Unlock()ます。

ミューテックスを使用する場合は、できるだけ早くロックを解除してください。ロックとロック解除の間のコードをできるだけ短くしてください。

m.Lock()
// Do what you need to do for the lock
mymap[key] = value
m.Unlock()
// Do everything else here

sync.RWMutexはsync.Mutexとは異なり、必要な数の同時リーダーを持つことができます (RLock は read-lock を表します)。ライターがロックを取得しようとするとすぐに、他のリーダーがロックを取得するのを防ぎ、既存のリーダーがロックを解放するのを待ちます。

あるいは、チャネルを使用して go ルーチン間で値を渡すこともできます。チャネルは多くの状況で機能し、奨励されています。同時実行の詳細については、Effective Goを参照してください。ただし、チャネルが常にすべての状況に適合するとは限らないため、状況によって異なります。

于 2013-08-08T03:02:05.180 に答える
1

他の誰かが変数を読んでいる間に誰かが変数を変更すると、競合状態になります。あなたの例では、変数foo/selfは多くのゴルーチンによって同時に読み取られますが、同時にアクセスされている間は誰も変更していないため、すべて問題ありません。

より興味深い例は、MyStruct追加の属性を持つ構造体attribute1です。その場合、属性にも同じ規則が適用されます。異なるゴルーチンから同時に読み取ることができます-たとえば、DoSomethingメソッドは の値も出力する可能性がありself.attribute1ます-しかし、その間は変更することはできません。

アクセス中に変数を変更できるようにしたい場合は、ある種の同期プリミティブが必要です。慣用的な Go のアプローチは、別のゴルーチンからのデータが必要なときはいつでも、単一のゴルーチンからアクセスされ、チャネルを介して通信するローカル変数のみを使用することにより、同じ変数への同時アクセスを完全に回避することです。

Go でも利用できる代替アプローチは、syncsync.Mutex などのパッケージのプリミティブです。このsync/atomicパッケージは、変数をアトミックにロード/保存/変更/比較および交換するために使用できる、より粒度の細かいプリミティブも提供します。

于 2013-08-08T10:59:17.893 に答える