C11 Standardının 5.1.2.4 Bölümü, özellikle de Serbest Bırakma / Edinme anlambilimi ile mücadele ediyorum. Şunu not ediyorum https://preshing.com/20120913/acquire-and-release-semantics/ (diğerleri arasında) olduğunu belirtmektedir:
... Sürüm semantiği, program sırasından önce gelen herhangi bir okuma veya yazma işlemi ile yazma sürümünün belleğin yeniden sıralanmasını önler.
Yani, aşağıdakiler için:
typedef struct test_struct
{
_Atomic(bool) ready ;
int v1 ;
int v2 ;
} test_struct_t ;
extern void
test_init(test_struct_t* ts, int v1, int v2)
{
ts->v1 = v1 ;
ts->v2 = v2 ;
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
}
extern int
test_thread_1(test_struct_t* ts, int v2)
{
int v1 ;
while (atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v2 = v2 ; // expect read to happen before store/release
v1 = ts->v1 ; // expect write to happen before store/release
atomic_store_explicit(&ts->ready, true, memory_order_release) ;
return v1 ;
}
extern int
test_thread_2(test_struct_t* ts, int v1)
{
int v2 ;
while (!atomic_load_explicit(&ts->ready, memory_order_acquire)) ;
ts->v1 = v1 ;
v2 = ts->v2 ; // expect write to happen after store/release in thread "1"
atomic_store_explicit(&ts->ready, false, memory_order_release) ;
return v2 ;
}
bunlar yürütüldüğünde:
> in the "main" thread: test_struct_t ts ;
> test_init(&ts, 1, 2) ;
> start thread "2" which does: r2 = test_thread_2(&ts, 3) ;
> start thread "1" which does: r1 = test_thread_1(&ts, 4) ;
Bu nedenle, "1" iş parçacığının r1 == 1 olmasını ve "2" iş parçacığının r2 = 4 olmasını beklerdim.
Bunu beklerim çünkü (bölüm 5.1.2.4'ün 16. ve 18. paragrafları):
- (atomik olmayan) tüm okuma ve yazma işlemleri "önce sıralanır" ve bu nedenle "1" iş parçacığında atomik yazma / bırakmadan önce "olur,
- hangi "thread-inter-thread-before-" "atomik" 2 "dizisinde okuma / alma ('true' okuduğunda),
- bu da "daha önce dizilenir" ve dolayısıyla "atomik değil" ("2" iş parçacığında) okuma ve yazma işleminden önce gerçekleşir.
Ancak, standardı anlayamıyorum tamamen mümkündür.
X86_64 için oluşturulan kod şunları içerir:
test_thread_1:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
jne <test_thread_1> -- while is true
mov %esi,0x8(%rdi) -- (W1) ts->v2 = v2
mov 0x4(%rdi),%eax -- (R1) v1 = ts->v1
movb $0x1,(%rdi) -- (X1) atomic_store_explicit(&ts->ready, true, memory_order_release)
retq
test_thread_2:
movzbl (%rdi),%eax -- atomic_load_explicit(&ts->ready, memory_order_acquire)
test $0x1,%al
je <test_thread_2> -- while is false
mov %esi,0x4(%rdi) -- (W2) ts->v1 = v1
mov 0x8(%rdi),%eax -- (R2) v2 = ts->v2
movb $0x0,(%rdi) -- (X2) atomic_store_explicit(&ts->ready, false, memory_order_release)
retq
Ve R1 ve X1'in bu sırayla gerçekleşmesi koşuluyla , bu beklediğim sonucu verir.
Ama benim x86_64 anlayışım, okumaların diğer okumalara göre gerçekleşmesi ve diğer yazmalara göre gerçekleşmesi, ancak okumaların ve yazmaların birbiri ile gerçekleşmeyebileceğidir. Bu, X1'in R1'den önce gerçekleşmesi ve hatta X1, X2, W2, R1'in bu sırayla gerçekleşmesi anlamına geliyor - inanıyorum. [Bu umutsuzca görünüyor, ama eğer R1 bazı önbellek sorunları tarafından tutulduysa?]
Lütfen: neyi anlamıyorum?
Ben bir sürü / depolarını değiştirirseniz dikkat ts->ready
etmek memory_order_seq_cst
, mağazalar için üretilen kod şudur:
xchg %cl,(%rdi)
ki bu benim x86_64 anlayışımla tutarlı ve beklediğim sonucu verecek.
8.2.3.3 Stores Are Not Reordered With Earlier Loads
. Böylece derleyiciniz kodunuzu doğru şekilde çeviriyor (ne kadar şaşırtıcı), böylece kodunuz etkili bir şekilde tamamen sıralı olacak ve aynı anda ilginç bir şey olmayacak.