6
func resetElectionTimeoutMS(newMin, newMax int) (int, int) {
    oldMin := atomic.LoadInt32(&MinimumElectionTimeoutMS)
    oldMax := atomic.LoadInt32(&maximumElectionTimeoutMS)
    atomic.StoreInt32(&MinimumElectionTimeoutMS, int32(newMin))
    atomic.StoreInt32(&maximumElectionTimeoutMS, int32(newMax))
    return int(oldMin), int(oldMax)
}

私はこのようなgoコード関数を手に入れました。私が混乱しているのは、なぜatomicここに必要なのですか? これは何を防いでいるのですか?

ありがとう。

4

2 に答える 2

15

アトミック関数は、タスクのすべての部分が瞬時に発生するか、まったく発生しないように見える、孤立した方法でタスクを完了します。

この場合、LoadInt32 と StoreInt32 は、誰かがロードしても部分ストアが取得されないように、整数が格納および取得されることを保証します。ただし、これが正しく機能するには、両方の側でアトミック関数を使用する必要があります。いかだの例は、少なくとも 2 つの理由で正しくないように見えます。

  1. 2 つのアトミック関数は 1 つのアトミック関数として機能しないため、古いものを読み取り、新しいものを 2 行で設定すると、競合状態になります。あなたが読んだ後、他の誰かが設定し、次にあなたが設定すると、設定する前に以前の値に対して誤った情報を返している可能性があります。

  2. MinimumElectionTimeoutMS にアクセスするすべての人がアトミック操作を使用しているわけではありません。これは、この関数でのアトミックの使用が事実上役に立たないことを意味します。

これはどのように修正されますか?

func resetElectionTimeoutMS(newMin, newMax int) (int, int) {
    oldMin := atomic.SwapInt32(&MinimumElectionTimeoutMS, int32(newMin))
    oldMax := atomic.SwapInt32(&maximumElectionTimeoutMS, int32(newMax))
    return int(oldMin), int(oldMax)
}

これにより、oldMin がスワップ前に存在していた最小値になります。ただし、最終的な結果は、resetElectionTimeoutMS で呼び出されたことのない oldMin と oldMax のペアになる可能性があるため、関数全体は依然としてアトミックではありません。そのためには...ロックを使用してください。

アトミック ロードを実行するには、各関数も変更する必要があります。

func minimumElectionTimeout() time.Duration {
    min := atomic.LoadInt32(&MinimumElectionTimeoutMS)
    return time.Duration(min) * time.Millisecond
}

golang のアトミック ドキュメントで言及されている VonC の引用を慎重に検討することをお勧めします。

これらの機能を正しく使用するには細心の注意が必要です。特別な低レベルのアプリケーションを除いて、同期はチャネルまたは sync パッケージの機能を使用して行う方が適切です。

アトミック操作を理解したい場合は、http://preshing.com/20130618/atomic-vs-non-atomic-operations/から始めることをお勧めします。これは、例で使用されているロードおよびストア操作を超えています。ただし、アトミックには他にも用途があります。go atomic パッケージの概要では、atomic swapping (私が示した例)、compare and swap (CAS として知られている)、Adding などのいくつかの優れた機能について説明しています。

私があなたに与えたリンクからの面白い引用:

x86 では、32 ビットの mov 命令は、メモリ オペランドが自然にアラインされている場合はアトミックですが、それ以外の場合は非アトミックであることはよく知られています。つまり、原子性は、32 ビット整数が正確に 4 の倍数であるアドレスにある場合にのみ保証されます。

言い換えれば、今日の一般的なシステムでは、例で使用されているアトミック関数は事実上ノーオペレーションです。彼らはすでにアトミックです!(ただし、それらは保証されていません。アトミックにする必要がある場合は、明示的に指定することをお勧めします)

于 2014-08-27T06:24:15.783 に答える
1

パッケージatomicが同期アルゴリズムの実装に役立つ低レベルのアトミック メモリ プリミティブを提供することを考えると、次のように使用することを意図していたと思います。

  • MinimumElectionTimeoutMSに保存されている間は変更されませんoldMin
  • MinimumElectionTimeoutMS新しい値に設定されている間は変更されませんnewMin

ただし、パッケージには次の警告が表示されます。

これらの機能を正しく使用するには細心の注意が必要です。
特別な低レベルのアプリケーションを除いて、同期はチャネルまたは sync パッケージの機能を使用して行う方が適切です。
通信によってメモリを共有します。メモリを共有して通信しないでください。

この場合 ( Raft 分散コンセンサス プロトコルserver.goから)、変数を直接同期することは、すべての関数を配置するよりも高速であると見なされる可能性があります。Mutex

ただし、Stephen Weinbergの回答 (賛成) が示すように、これはアトミックの使用方法ではありません。oldMinスワップを行っている間、それが正確であることを確認するだけです。

「メモリモデル」に関連する「 Is the two atom style code in sync/atomic.once.goneed? 」の別の例を参照してください。


OneOfOneは、アトミック CAS をスピンロック (非常に高速なロック) として使用するコメントで言及しています。

BenchmarkSpinL-8            2000            708494 ns/op           32315 B/op       2001 allocs/op
BenchmarkMutex-8            1000           1225260 ns/op           78027 B/op       2259 allocs/op

見る:

于 2014-08-27T06:10:20.043 に答える