アトミック関数は、タスクのすべての部分が瞬時に発生するか、まったく発生しないように見える、孤立した方法でタスクを完了します。
この場合、LoadInt32 と StoreInt32 は、誰かがロードしても部分ストアが取得されないように、整数が格納および取得されることを保証します。ただし、これが正しく機能するには、両方の側でアトミック関数を使用する必要があります。いかだの例は、少なくとも 2 つの理由で正しくないように見えます。
2 つのアトミック関数は 1 つのアトミック関数として機能しないため、古いものを読み取り、新しいものを 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 の倍数であるアドレスにある場合にのみ保証されます。
言い換えれば、今日の一般的なシステムでは、例で使用されているアトミック関数は事実上ノーオペレーションです。彼らはすでにアトミックです!(ただし、それらは保証されていません。アトミックにする必要がある場合は、明示的に指定することをお勧めします)