6

マップから削除できるライターを含め、ライターが同時に存在するマップにまたがる場合m、これを行うのはスレッドセーフではありませんか?:

for k, v := range m { ... }

スレッドセーフだと思っています。他の可能性のあるライターが値vを読み取っているときに値を変更しないようにする必要があります。また、(ミューテックスを使用している場合、ロックは別の手順であるため)キーkがまだマップにあることを確認します。 。例えば:

for k := range m {
    m.mutex.RLock()
    v, found := m[k]
    m.mutex.RUnlock()
    if found {
        ... // process v
    }
}

m(他のライターが変更する前に書き込みロックしていると仮定しvます。)より良い方法はありますか?

編集して追加:マップがスレッドセーフではないことを認識しています。ただし、http: //golang.org/ref/spec#For_statementsのGo仕様によると、これらは1つの方法でスレッドセーフです(「まだ到達していないマップエントリが反復中に削除された場合」を検索してください)。このページは、を使用するコードrangeが、他のゴルーチンがマップに挿入したり、マップから削除したりすることを気にする必要がないことを示しています。私の質問は、このスレッドセーフネスはに拡張されているので、使用するだけで読み取りvを行うことができますか?v for k, v := range mそして他のスレッドセーフなメカニズムはありませんか?アプリを強制的にクラッシュさせて動作しないことを証明するためのテストコードを作成しましたが、スレッドセーフでないコードを露骨に実行していても(ロックメカニズムがない状態で同じマップ値を猛烈に変更するゴルーチンがたくさんあります)、できませんでしたクラッシュに行く!

4

2 に答える 2

9

いいえ、マップ操作はアトミック/スレッドセーフではありません。質問へのコメント投稿者がgolang FAQ「マップ操作がアトミックとして定義されていないのはなぜですか?」</a>を指摘しているためです。

アクセスを保護するために、Goを使用することをお勧めしますリソースアクセストークンの手段としてのチャネル。チャネルは、単にトークンを渡すために使用されます。それを変更したい人は誰でも、チャネルから変更を要求します-ブロッキングまたは非ブロッキング。マップの操作が完了すると、トークンがチャネルに返されます。

マップの反復と操作は十分に単純で短い必要があるため、フルアクセスにはトークンを1つだけ使用しても問題ありません。

そうではなく、より複雑なものにマップを使用する場合/リソースコンシューマーがより多くの時間を必要とする場合は、リーダーアクセストークンとライターアクセストークンを実装できます。したがって、常に1人のライターのみがマップにアクセスできますが、アクティブなライターがいない場合、トークンは任意の数のリーダーに渡され、リーダーはマップを変更しません(したがって、同時に読み取ることができます)。

チャネルの概要については、チャネルに関する効果的なGoドキュメントを参照してください。

于 2012-10-17T19:37:45.637 に答える
0

並行マップを使用して、並行性の問題を処理できます。

// Create a new map.
map := cmap.NewConcurretMap()

// Add item to map, adds "bar" under key "foo"
map.Add("foo", "bar")

// Retrieve item from map.
tmp, ok := map.Get("foo")

// Checks if item exists
if ok == true {
    // Map stores items as interface{}, hence we'll have to cast.
    bar := tmp.(string)
}

// Removes item under key "foo"
map.Remove("foo")
于 2014-10-05T16:50:55.427 に答える