Odpowiedź Scheffa opisuje, jak naprawić twój kod. Pomyślałem, że dodam trochę informacji o tym, co faktycznie dzieje się w tej sprawie.
Skompilowałem Twój kod na Godbolt, używając poziomu optymalizacji 1 ( -O1
). Twoja funkcja kompiluje się tak:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Co się tu dzieje? Po pierwsze, mamy porównanie: cmp BYTE PTR finished[rip], 0
- to sprawdza, czy finished
jest fałszywe, czy nie.
Jeśli nie jest to fałsz (inaczej prawda), powinniśmy wyjść z pętli przy pierwszym uruchomieniu. To osiągnąć przez jne .L4
który j umps gdy n ot e qual do etykiety .L4
, gdy wartość i
( 0
) przechowywana jest w rejestrze do późniejszego wykorzystania i powrót funkcji.
Jeśli jednak jest to fałsz, przechodzimy do
.L5:
jmp .L5
Jest to bezwarunkowy skok, .L5
którego etykietą jest tak naprawdę samo polecenie skoku.
Innymi słowy, wątek jest umieszczany w nieskończonej zajętej pętli.
Dlaczego to się stało?
Jeśli chodzi o optymalizator, wątki są poza jego zakresem. Zakłada, że inne wątki nie odczytują ani nie zapisują zmiennych jednocześnie (ponieważ byłoby to UB wyścigu danych). Musisz powiedzieć, że nie może zoptymalizować dostępu. Właśnie tutaj pojawia się odpowiedź Scheffa. Nie zawracam sobie głowy powtórzeniem go.
Ponieważ optymalizatorowi nie powiedziano, że finished
zmienna może potencjalnie ulec zmianie podczas wykonywania funkcji, widzi, że finished
nie jest modyfikowana przez samą funkcję i zakłada, że jest stała.
Zoptymalizowany kod zapewnia dwie ścieżki kodu, które będą wynikały z wejścia do funkcji ze stałą wartością bool; albo uruchamia pętlę nieskończenie, albo pętla nigdy nie jest uruchamiana.
w -O0
kompilatorze (zgodnie z oczekiwaniami) nie optymalizuje treści pętli i porównuje:
func():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], 0
.L148:
movzx eax, BYTE PTR finished[rip]
test al, al
jne .L147
add QWORD PTR [rbp-8], 1
jmp .L148
.L147:
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
dlatego funkcja, gdy niezoptymalizowana działa, brak atomowości tutaj zwykle nie stanowi problemu, ponieważ kod i typ danych są proste. Prawdopodobnie najgorsze, na co moglibyśmy się tutaj natknąć, to wartość, i
która nie jest zgodna z tym, co powinno być.
Bardziej złożony system ze strukturami danych znacznie bardziej prawdopodobne jest uszkodzenie danych lub nieprawidłowe wykonanie.