web-dev-qa-db-ja.com

golangでポインタアトミックを割り当てていますか?

Goでポインタをアトミックに割り当てていますか?

ロックにポインタを割り当てる必要がありますか?ポインタをnilに割り当てたいだけで、他のスレッドがそれを認識できるようにしたいとします。 Javaで、これにvolatileを使用できることはわかっていますが、goにはvolatileはありません。

ありがとう。

20
Koda

Goでアトミックであることが保証されているのは、 sync.atomic の操作だけです。

したがって、確実にしたい場合は、ロックを取得する必要があります。たとえば、 sync.Mutex か、アトミックプリミティブの1つを使用します。アトミックプリミティブを使用することはお勧めしません。ポインターを使用するすべての場所で使用する必要があり、正しく取得するのが難しいためです。

ミューテックスの使用はOKゴースタイルです-ロックを使用して現在のポインタを返す関数を非常に簡単に定義できます。

import "sync"

var secretPointer *int
var pointerLock sync.Mutex

func CurrentPointer() *int {
    pointerLock.Lock()
    defer pointerLock.Unlock()
    return secretPointer
}

func SetPointer(p *int) {
    pointerLock.Lock()
    secretPointer = p
    pointerLock.Unlock()
}

これらの関数は、マスターポインターが変更されても一定のままである、クライアントへのポインターのコピーを返します。これは、要件の重要度に応じて、受け入れられる場合と受け入れられない場合があります。未定義の動作を回避するだけで十分です。ガベージコレクタは、ポイントされたメモリがプログラムで使用されなくなった場合でも、ポインタが常に有効であるようにします。

別のアプローチは、1つのgoルーチンからのポインターアクセスのみを実行し、チャネルを使用して、goルーチンが処理を実行するように命令することです。これはより慣用的な方法と見なされますが、アプリケーションに正確に適合しない場合があります。

更新

ここに例がありますatomic.SetPointerの使用方法を示しています。 unsafe.Pointerを使用しているため、かなり醜いです。ただし、unsafe.Pointerはコンパイルを何もキャストしないため、実行時のコストは小さくなります。

import (
    "fmt"
    "sync/atomic"
    "unsafe"
)

type Struct struct {
    p unsafe.Pointer // some pointer
}

func main() {
    data := 1

    info := Struct{p: unsafe.Pointer(&data)}

    fmt.Printf("info is %d\n", *(*int)(info.p))

    otherData := 2

    atomic.StorePointer(&info.p, unsafe.Pointer(&otherData))

    fmt.Printf("info is %d\n", *(*int)(info.p))

}
16
Nick Craig-Wood

仕様では指定されていないため、指定されていないと想定する必要があります。現在アトミックである場合でも、仕様に違反することなく変更される可能性があります。

6
Jeremy Wall