<stdatomic.h> Các Hàm Liên Quan Đến Atomic| Hàm | Mô tả |
|---|---|
atomic_compare_exchange_strong_explicit() |
Compare-and-exchange nguyên tử, strong, explicit |
atomic_compare_exchange_strong() |
Compare-and-exchange nguyên tử, strong |
atomic_compare_exchange_weak_explicit() |
Compare-and-exchange nguyên tử, weak, explicit |
atomic_compare_exchange_weak() |
Compare-and-exchange nguyên tử, weak |
atomic_exchange_explicit() |
Thay giá trị trong một object nguyên tử, explicit |
atomic_exchange() |
Thay giá trị trong một object nguyên tử |
atomic_fetch_add_explicit() |
Cộng nguyên tử vào một số nguyên atomic, explicit |
atomic_fetch_add() |
Cộng nguyên tử vào một số nguyên atomic |
atomic_fetch_and_explicit() |
AND bit nguyên tử một số nguyên atomic, explicit |
atomic_fetch_and() |
AND bit nguyên tử một số nguyên atomic |
atomic_fetch_or_explicit() |
OR bit nguyên tử một số nguyên atomic, explicit |
atomic_fetch_or() |
OR bit nguyên tử một số nguyên atomic |
atomic_fetch_sub_explicit() |
Trừ nguyên tử khỏi một số nguyên atomic, explicit |
atomic_fetch_sub() |
Trừ nguyên tử khỏi một số nguyên atomic |
atomic_fetch_xor_explicit() |
XOR bit nguyên tử một số nguyên atomic, explicit |
atomic_fetch_xor() |
XOR bit nguyên tử một số nguyên atomic |
atomic_flag_clear_explicit() |
Xoá một atomic flag, explicit |
atomic_flag_clear() |
Xoá một atomic flag |
atomic_flag_test_and_set_explicit() |
Test-and-set một atomic flag, explicit |
atomic_flag_test_and_set() |
Test-and-set một atomic flag |
atomic_init() |
Khởi tạo một biến atomic |
atomic_is_lock_free() |
Xác định xem một kiểu atomic có lock-free không |
atomic_load_explicit() |
Trả về giá trị từ một biến atomic, explicit |
atomic_load() |
Trả về giá trị từ một biến atomic |
atomic_signal_fence() |
Fence cho signal handler trong cùng thread |
atomic_store_explicit() |
Lưu một giá trị vào biến atomic, explicit |
atomic_store() |
Lưu một giá trị vào biến atomic |
atomic_thread_fence() |
Dựng một fence |
ATOMIC_VAR_INIT() |
Tạo initializer cho một biến atomic |
kill_dependency() |
Kết thúc một chuỗi dependency |
Trên các hệ điều hành kiểu Unix, bạn có thể cần thêm -latomic vào dòng lệnh biên dịch.
Header này định nghĩa sẵn một đống kiểu:
| Kiểu atomic | Dạng đầy đủ tương đương |
|---|---|
atomic_bool |
_Atomic _Bool |
atomic_char |
_Atomic char |
atomic_schar |
_Atomic signed char |
atomic_uchar |
_Atomic unsigned char |
atomic_short |
_Atomic short |
atomic_ushort |
_Atomic unsigned short |
atomic_int |
_Atomic int |
atomic_uint |
_Atomic unsigned int |
atomic_long |
_Atomic long |
atomic_ulong |
_Atomic unsigned long |
atomic_llong |
_Atomic long long |
atomic_ullong |
_Atomic unsigned long long |
atomic_char16_t |
_Atomic char16_t |
atomic_char32_t |
_Atomic char32_t |
atomic_wchar_t |
_Atomic wchar_t |
atomic_int_least8_t |
_Atomic int_least8_t |
atomic_uint_least8_t |
_Atomic uint_least8_t |
atomic_int_least16_t |
_Atomic int_least16_t |
atomic_uint_least16_t |
_Atomic uint_least16_t |
atomic_int_least32_t |
_Atomic int_least32_t |
atomic_uint_least32_t |
_Atomic uint_least32_t |
atomic_int_least64_t |
_Atomic int_least64_t |
atomic_uint_least64_t |
_Atomic uint_least64_t |
atomic_int_fast8_t |
_Atomic int_fast8_t |
atomic_uint_fast8_t |
_Atomic uint_fast8_t |
atomic_int_fast16_t |
_Atomic int_fast16_t |
atomic_uint_fast16_t |
_Atomic uint_fast16_t |
atomic_int_fast32_t |
_Atomic int_fast32_t |
atomic_uint_fast32_t |
_Atomic uint_fast32_t |
atomic_int_fast64_t |
_Atomic int_fast64_t |
atomic_uint_fast64_t |
_Atomic uint_fast64_t |
atomic_intptr_t |
_Atomic intptr_t |
atomic_uintptr_t |
_Atomic uintptr_t |
atomic_size_t |
_Atomic size_t |
atomic_ptrdiff_t |
_Atomic ptrdiff_t |
atomic_intmax_t |
_Atomic intmax_t |
atomic_uintmax_t |
_Atomic uintmax_t |
Bạn có thể tự làm thêm kiểu của mình bằng type qualifier _Atomic:
_Atomic double x;hoặc type specifier _Atomic():
_Atomic(double) x;Các macro này cho bạn biết một kiểu có lock-free hay không. Có thể.
Chúng có thể dùng ở compile time với #if. Chúng áp dụng cho cả kiểu signed và unsigned.
| Kiểu Atomic | Macro Lock-Free |
|---|---|
atomic_bool |
ATOMIC_BOOL_LOCK_FREE |
atomic_char |
ATOMIC_CHAR_LOCK_FREE |
atomic_char16_t |
ATOMIC_CHAR16_T_LOCK_FREE |
atomic_char32_t |
ATOMIC_CHAR32_T_LOCK_FREE |
atomic_wchar_t |
ATOMIC_WCHAR_T_LOCK_FREE |
atomic_short |
ATOMIC_SHORT_LOCK_FREE |
atomic_int |
ATOMIC_INT_LOCK_FREE |
atomic_long |
ATOMIC_LONG_LOCK_FREE |
atomic_llong |
ATOMIC_LLONG_LOCK_FREE |
atomic_intptr_t |
ATOMIC_POINTER_LOCK_FREE |
Thú vị là các macro này có thể có tới ba giá trị khác nhau:
| Giá trị | Ý nghĩa |
|---|---|
0 |
Không bao giờ lock-free. |
1 |
Đôi khi lock-free38. |
2 |
Luôn lock-free. |
Kiểu mờ (opaque type) atomic_flag là thứ duy nhất được đảm bảo là lock-free. Dù implementation trên PC của bạn chắc sẽ làm được nhiều hơn thế.
Nó được truy cập qua các hàm atomic_flag_test_and_set() và atomic_flag_clear().
Trước khi dùng, có thể khởi tạo nó về trạng thái clear bằng:
atomic_flag f = ATOMIC_FLAG_INIT;Header này giới thiệu một kiểu enum mới tên là memory_order. Nó được dùng bởi một đống hàm để chỉ định các memory order (thứ tự bộ nhớ) khác với sequential consistency (tính nhất quán tuần tự).
memory_order |
Mô tả |
|---|---|
memory_order_seq_cst |
Sequential Consistency |
memory_order_acq_rel |
Acquire/Release |
memory_order_release |
Release |
memory_order_acquire |
Acquire |
memory_order_consume |
Consume |
memory_order_relaxed |
Relaxed |
Bạn có thể truyền mấy thứ này vào các hàm atomic có hậu tố _explicit.
Các phiên bản không có _explicit hoạt động y như khi bạn gọi phiên bản _explicit tương ứng với memory_order_seq_cst.
ATOMIC_VAR_INIT()Tạo một initializer cho biến atomic
#include <stdatomic.h>
#define ATOMIC_VAR_INIT(C value) // DeprecatedMacro này mở rộng thành một initializer, nên bạn có thể dùng nó khi định nghĩa biến.
Kiểu của value phải là kiểu cơ sở của biến atomic.
Buồn cười là, bản thân việc khởi tạo không phải thao tác nguyên tử (atomic).
CPPReference nói rằng cái này đã bị deprecated39 và nhiều khả năng sẽ bị bỏ. Tài liệu tiêu chuẩn p1138r040 giải thích thêm rằng macro này bị hạn chế ở chỗ không thể khởi tạo đúng các atomic struct, và lý do tồn tại ban đầu của nó hoá ra chẳng hữu dụng.
Cứ khởi tạo biến trực tiếp đi là xong.
Mở rộng thành initializer phù hợp cho biến atomic này.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x = ATOMIC_VAR_INIT(3490); // Deprecated
printf("%d\n", x);
}atomic_init()Khởi tạo một biến atomic
#include <stdatomic.h>
void atomic_init(volatile A *obj, C value);Bạn có thể dùng nó để khởi tạo một biến atomic.
Kiểu của value phải là kiểu cơ sở của biến atomic.
Buồn cười là, bản thân việc khởi tạo không phải thao tác nguyên tử.
Theo như tôi thấy, chẳng có gì khác biệt giữa cái này và việc gán trực tiếp cho biến atomic. Spec nói nó có mặt để cho phép compiler chèn thêm bất kỳ việc khởi tạo bổ sung nào cần làm, nhưng mọi thứ vẫn ổn nếu không có nó. Nếu ai đó có thêm thông tin, gửi cho tôi nhé.
Không trả về gì cả!
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x;
atomic_init(&x, 3490);
printf("%d\n", x);
}ATOMIC_VAR_INIT(), atomic_store(), atomic_store_explicit()
kill_dependency()Kết thúc một chuỗi dependency
#include <stdatomic.h>
type kill_dependency(type y);Cái này có khả năng hữu ích để tối ưu nếu bạn đang dùng memory_order_consume ở đâu đó.
Và nếu bạn biết mình đang làm gì. Nếu không chắc, tìm hiểu thêm trước khi thử dùng.
Trả về giá trị được truyền vào.
Trong ví dụ này, i mang theo một dependency vào x. Và cũng sẽ mang vào y, nhưng vì có lời gọi kill_dependency() nên không.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int a;
int i = 10, x, y;
atomic_store_explicit(&a, 3490, memory_order_release);
i = atomic_load_explicit(&a, memory_order_consume);
x = i;
y = kill_dependency(i);
printf("%d %d\n", x, y); // 3490 and either 3490 or 10
}atomic_thread_fence()Dựng một fence (hàng rào)
#include <stdatomic.h>
void atomic_thread_fence(memory_order order);Hàm này dựng một memory fence (rào cản bộ nhớ) với order chỉ định.
order |
Mô tả |
|---|---|
memory_order_seq_cst |
Fence acquire/release theo sequential consistency |
memory_order_acq_rel |
Fence acquire/release |
memory_order_release |
Fence release |
memory_order_acquire |
Fence acquire |
memory_order_consume |
Fence acquire (again) |
memory_order_relaxed |
Không có fence gì cả—gọi với cái này chẳng có ý nghĩa gì |
Bạn có thể cố tránh dùng mấy cái này và cứ bám vào các chế độ khác nhau với atomic_store_explicit() và atomic_load_explicit(). Hoặc không.
Không trả về gì cả!
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
atomic_int shared_1 = 1;
atomic_int shared_2 = 2;
int thread_1(void *arg)
{
(void)arg;
atomic_store_explicit(&shared_1, 10, memory_order_relaxed);
atomic_thread_fence(memory_order_release);
atomic_store_explicit(&shared_2, 20, memory_order_relaxed);
return 0;
}
int thread_2(void *arg)
{
(void)arg;
// If this fence runs after the release fence, we're
// guaranteed to see thread_1's changes to the shared
// varaibles.
atomic_thread_fence(memory_order_acquire);
if (shared_2 == 20) {
printf("Shared_1 better be 10 and it's %d\n", shared_1);
} else {
printf("Anything's possible: %d %d\n", shared_1, shared_2);
}
return 0;
}
int main(void)
{
thrd_t t1, t2;
thrd_create(&t2, thread_2, NULL);
thrd_create(&t1, thread_1, NULL);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
}atomic_store_explicit(), atomic_load_explicit(), atomic_signal_fence()
atomic_signal_fence()Fence cho signal handler trong cùng thread
#include <stdatomic.h>
void atomic_signal_fence(memory_order order);Hàm này hoạt động giống atomic_thread_fence() nhưng mục đích là trong phạm vi một thread duy nhất; đáng chú ý là để dùng trong signal handler của thread đó.
Vì signal có thể xảy ra bất cứ lúc nào, ta có thể cần một cách để chắc chắn rằng mọi write của thread xảy ra trước signal handler sẽ nhìn thấy được bên trong signal handler đó.
Không trả về gì cả!
Demo một phần. (Lưu ý rằng về mặt kỹ thuật thì việc gọi printf() trong signal handler là undefined behavior.)
#include <stdio.h>
#include <signal.h>
#include <stdatomic.h>
int global;
void handler(int sig)
{
(void)sig;
// If this runs before the release, the handler will
// potentially see global == 0.
//
// Otherwise, it will definitely see global == 10.
atomic_signal_fence(memory_order_acquire);
printf("%d\n", global);
}
int main(void)
{
signal(SIGINT, handler);
global = 10;
atomic_signal_fence(memory_order_release);
// If the signal handler runs after the release
// it will definitely see the value 10 in global.
}atomic_thread_fence(), signal()
atomic_is_lock_free()Xác định xem một kiểu atomic có lock-free không
#include <stdatomic.h>
_Bool atomic_is_lock_free(const volatile A *obj);Xác định xem biến obj kiểu A có lock-free không. Dùng được với bất kỳ kiểu nào.
Khác với các macro lock-free có thể dùng ở compile-time, đây thuần tuý là hàm run-time. Vì vậy ở những chỗ mà macro trả lời “có thể”, hàm này sẽ chắc chắn cho bạn biết biến atomic có lock-free hay không.
Cái này hữu ích khi bạn tự định nghĩa biến atomic của mình và muốn biết trạng thái lock-free của chúng.
True nếu biến lock-free, false nếu không.
Kiểm tra xem một cặp struct và một double atomic có lock-free không. Trên hệ thống của tôi, struct lớn hơn thì to quá không lock-free được, nhưng hai cái còn lại thì OK.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
struct foo {
int x, y;
};
struct bar {
int x, y, z;
};
_Atomic(double) a;
struct foo b;
struct bar c;
printf("a is lock-free: %d\n", atomic_is_lock_free(&a));
printf("b is lock-free: %d\n", atomic_is_lock_free(&b));
printf("c is lock-free: %d\n", atomic_is_lock_free(&c));
}Output trên hệ thống của tôi (YMMV):
a is lock-free: 1
b is lock-free: 1
c is lock-free: 0atomic_store()Lưu một giá trị vào biến atomic
#include <stdatomic.h>
void atomic_store(volatile A *object, C desired);
void atomic_store_explicit(volatile A *object,
C desired, memory_order order);Lưu một giá trị vào biến atomic, có thể được đồng bộ.
Cái này giống như một phép gán thông thường, nhưng linh hoạt hơn.
Mấy cái sau có cùng hiệu ứng lưu trữ với một atomic_int x:
x = 10;
atomic_store(&x, 10);
atomic_store_explicit(&x, 10, memory_order_seq_cst);Nhưng hàm cuối, atomic_store_explicit(), cho bạn chỉ định memory order.
Vì đây là thao tác kiểu “release-y”, không có memory order kiểu “acquire-y” nào hợp lệ. order chỉ có thể là memory_order_seq_cst, memory_order_release, hoặc memory_order_relaxed.
order không thể là memory_order_acq_rel, memory_order_acquire, hoặc memory_order_consume.
Không trả về gì cả!
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x = 0;
atomic_int y = 0;
atomic_store(&x, 10);
atomic_store_explicit(&y, 20, memory_order_relaxed);
// Will print either "10 20" or "10 0":
printf("%d %d\n", x, y);
}atomic_init(), atomic_load(), atomic_load_explicit(), atomic_exchange(),
atomic_exchange_explicit(), atomic_compare_exchange_strong(),
atomic_compare_exchange_strong_explicit(), atomic_compare_exchange_weak(),
atomic_compare_exchange_weak_explicit(), atomic_fetch_*()
atomic_load()Trả về giá trị từ một biến atomic
#include <stdatomic.h>
C atomic_load(const volatile A *object);
C atomic_load_explicit(const volatile A *object, memory_order order);Với một con trỏ tới object kiểu A, nguyên tử trả về giá trị C của nó. Đây là hàm generic có thể dùng với bất kỳ kiểu nào.
Hàm atomic_load_explicit() cho bạn chỉ định memory order.
Vì đây là thao tác kiểu “acquire-y”, không có memory order kiểu “release-y” nào hợp lệ. order chỉ có thể là memory_order_seq_cst, memory_order_acquire, memory_order_consume, hoặc memory_order_relaxed.
order không thể là memory_order_acq_rel hoặc memory_order_release.
Trả về giá trị được lưu trong object.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x = 10;
int v = atomic_load(&x);
printf("%d\n", v); // 10
}atomic_store(), atomic_store_explicit()
atomic_exchange()Thay giá trị trong một object nguyên tử
#include <stdatomic.h>
C atomic_exchange(volatile A *object, C desired);
C atomic_exchange_explicit(volatile A *object, C desired,
memory_order order);Đặt giá trị trong object thành desired.
object có kiểu A, một kiểu atomic nào đó.
desired có kiểu C, kiểu non-atomic tương ứng với A.
Cái này rất giống atomic_store(), trừ việc giá trị trước đó được trả về một cách nguyên tử.
Trả về giá trị trước đó của object.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x = 10;
int previous = atomic_exchange(&x, 20);
printf("x is %d\n", x);
printf("x was %d\n", previous);
}Output:
x is 20
x was 10atomic_init(), atomic_load(), atomic_load_explicit(), atomic_store(),
atomic_store_explicit() atomic_compare_exchange_strong(),
atomic_compare_exchange_strong_explicit(), atomic_compare_exchange_weak(),
atomic_compare_exchange_weak_explicit()
atomic_compare_exchange_*()Compare-and-exchange nguyên tử
#include <stdatomic.h>
_Bool atomic_compare_exchange_strong(volatile A *object,
C *expected, C desired);
_Bool atomic_compare_exchange_strong_explicit(volatile A *object,
C *expected, C desired,
memory_order success,
memory_order failure);
_Bool atomic_compare_exchange_weak(volatile A *object,
C *expected, C desired);
_Bool atomic_compare_exchange_weak_explicit(volatile A *object,
C *expected, C desired,
memory_order success,
memory_order failure);Nền tảng lâu đời cho vô số thứ lock-free: compare-and-exchange (CAS).
Trong các prototype ở trên, A là kiểu của object atomic, và C là kiểu cơ sở tương đương.
Bỏ qua các phiên bản _explicit một lúc, các hàm này làm:
Nếu giá trị được trỏ tới bởi object bằng giá trị được trỏ tới bởi expected, thì giá trị được trỏ tới bởi object được đặt thành desired. Và hàm trả về true cho biết trao đổi đã diễn ra.
Ngược lại, giá trị được trỏ tới bởi expected (vâng, expected) được đặt thành desired và hàm trả về false cho biết trao đổi không diễn ra.
Pseudocode cho trao đổi sẽ trông như thế này41:
bool compare_exchange(atomic_A *object, C *expected, C desired)
{
if (*object is the same as *expected) {
*object = desired
return true
}
*expected = desired
return false
}Các biến thể _weak có thể thất bại một cách tự phát, nên ngay cả khi *object == *desired, nó có thể không đổi giá trị và sẽ trả về false. Vì thế bạn sẽ muốn đặt nó trong vòng lặp nếu dùng42.
Các biến thể _explicit có hai memory order: success nếu *object được đặt thành desired, và failure nếu không.
Đây là các hàm test-and-set, nên bạn có thể dùng memory_order_acq_rel với các biến thể _explicit.
Trả về true nếu *object là *expected. Ngược lại, false.
Một ví dụ gượng ép, nơi nhiều thread cộng 2 vào một giá trị chia sẻ theo cách lock-free.
(Ngoài đời thực thì tốt hơn là dùng += 2 để làm việc này, trừ khi bạn đang dùng phép thuật _explicit nào đó.)
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
#define LOOP_COUNT 10000
atomic_int value;
int run(void *arg)
{
(void)arg;
for(int i = 0; i < LOOP_COUNT; i++) {
int cur = value;
int next;
do {
next = cur + 2;
} while (!atomic_compare_exchange_strong(&value, &cur, next));
}
return 0;
}
int main(void)
{
thrd_t t1, t2;
thrd_create(&t1, run, NULL);
thrd_create(&t2, run, NULL);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
printf("%d should equal %d\n", value, LOOP_COUNT * 4);
}Chỉ cần thay cái này bằng value = value + 2 thôi là gây ra chuyện đạp dữ liệu.
atomic_load(), atomic_load_explicit(), atomic_store(), atomic_store_explicit(), atomic_exchange(), atomic_exchange_explicit(), atomic_fetch_*()
atomic_fetch_*()Sửa đổi biến atomic một cách nguyên tử
#include <stdatomic.h>
C atomic_fetch_KEY(volatile A *object, M operand);
C atomic_fetch_KEY_explicit(volatile A *object, M operand,
memory_order order);Thật ra đây là một nhóm gồm 10 hàm. Bạn thay KEY bằng một trong các từ dưới để thực hiện thao tác đó:
addsuborxorandVậy các hàm này có thể cộng hoặc trừ giá trị vào/khỏi một biến atomic, hoặc có thể thực hiện OR, XOR, hoặc AND bit trên chúng.
Dùng với các kiểu integer hoặc pointer. Dù spec có hơi mơ hồ về vấn đề này, các kiểu khác sẽ làm C không vui. Nó còn cố tránh undefined behavior với signed integer:
C18 §7.17.7.5 ¶3:
Đối với các kiểu signed integer, số học được định nghĩa dùng biểu diễn bù hai với quấn vòng im lặng khi tràn; không có kết quả undefined nào.
Trong synopsis ở trên, A là kiểu atomic, và M là kiểu non-atomic tương ứng với A (hoặc ptrdiff_t cho pointer atomic), và C là kiểu non-atomic tương ứng với A.
Ví dụ, đây là một số thao tác trên một atomic_int.
atomic_fetch_add(&x, 20);
atomic_fetch_sub(&x, 37);
atomic_fetch_xor(&x, 3490);Chúng tương đương +=, -=, |=, ^= và &=, trừ việc giá trị trả về là giá trị trước đó của object atomic. (Với các toán tử gán, giá trị của biểu thức là giá trị sau khi đánh giá.)
atomic_int x = 10;
int prev = atomic_fetch_add(&x, 20);
printf("%d %d\n", prev, x); // 10 30so với:
atomic_int x = 10;
int prev = (x += 20);
printf("%d %d\n", prev, x); // 30 30Và, tất nhiên, phiên bản _explicit cho phép bạn chỉ định memory order còn tất cả các toán tử gán đều là memory_order_seq_cst.
Trả về giá trị trước đó của object atomic trước khi sửa đổi.
#include <stdio.h>
#include <stdatomic.h>
int main(void)
{
atomic_int x = 0;
int prev;
atomic_fetch_add(&x, 3490);
atomic_fetch_sub(&x, 12);
atomic_fetch_xor(&x, 444);
atomic_fetch_or(&x, 12);
prev = atomic_fetch_and(&x, 42);
printf("%d %d\n", prev, x); // 3118 42
}atomic_exchange(), atomic_exchange_explicit(), atomic_compare_exchange_strong(),
atomic_compare_exchange_strong_explicit(), atomic_compare_exchange_weak(),
atomic_compare_exchange_weak_explicit()
atomic_flag_test_and_set()Test-and-set một atomic flag
#include <stdatomic.h>
_Bool atomic_flag_test_and_set(volatile atomic_flag *object);
_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
memory_order order);Một trong các hàm lâu đời đáng kính của lập trình lock-free, hàm này set atomic flag được chỉ định trong object, và trả về giá trị trước đó của flag.
Như thường lệ, _explicit cho phép bạn chỉ định memory order khác.
Trả về true nếu flag đã được set trước đó, và false nếu chưa.
Dùng test-and-set để cài đặt một spin lock43:
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
// Shared non-atomic struct
struct {
int x, y, z;
} s = {1, 2, 3};
atomic_flag f = ATOMIC_FLAG_INIT;
int run(void *arg)
{
int tid = *(int*)arg;
printf("Thread %d: waiting for lock...\n", tid);
while (atomic_flag_test_and_set(&f));
printf("Thread %d: got lock, s is {%d, %d, %d}\n", tid,
s.x, s.y, s.z);
s.x = (tid + 1) * 5 + 0;
s.y = (tid + 1) * 5 + 1;
s.z = (tid + 1) * 5 + 2;
printf("Thread %d: set s to {%d, %d, %d}\n", tid, s.x, s.y, s.z);
printf("Thread %d: releasing lock...\n", tid);
atomic_flag_clear(&f);
return 0;
}
int main(void)
{
thrd_t t1, t2;
int tid[] = {0, 1};
thrd_create(&t1, run, tid+0);
thrd_create(&t2, run, tid+1);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
}Output ví dụ (thay đổi giữa các lần chạy):
Thread 0: waiting for lock...
Thread 0: got lock, s is {1, 2, 3}
Thread 1: waiting for lock...
Thread 0: set s to {5, 6, 7}
Thread 0: releasing lock...
Thread 1: got lock, s is {5, 6, 7}
Thread 1: set s to {10, 11, 12}
Thread 1: releasing lock...atomic_flag_clear()Xoá một atomic flag
#include <stdatomic.h>
void atomic_flag_clear(volatile atomic_flag *object);
void atomic_flag_clear_explicit(volatile atomic_flag *object,
memory_order order);Xoá một atomic flag.
Như thường lệ, _explicit cho phép bạn chỉ định memory order khác.
Không trả về gì cả!
Dùng test-and-set để cài đặt một spin lock44:
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
// Shared non-atomic struct
struct {
int x, y, z;
} s = {1, 2, 3};
atomic_flag f = ATOMIC_FLAG_INIT;
int run(void *arg)
{
int tid = *(int*)arg;
printf("Thread %d: waiting for lock...\n", tid);
while (atomic_flag_test_and_set(&f));
printf("Thread %d: got lock, s is {%d, %d, %d}\n", tid,
s.x, s.y, s.z);
s.x = (tid + 1) * 5 + 0;
s.y = (tid + 1) * 5 + 1;
s.z = (tid + 1) * 5 + 2;
printf("Thread %d: set s to {%d, %d, %d}\n", tid, s.x, s.y, s.z);
printf("Thread %d: releasing lock...\n", tid);
atomic_flag_clear(&f);
return 0;
}
int main(void)
{
thrd_t t1, t2;
int tid[] = {0, 1};
thrd_create(&t1, run, tid+0);
thrd_create(&t2, run, tid+1);
thrd_join(t1, NULL);
thrd_join(t2, NULL);
}Output ví dụ (thay đổi giữa các lần chạy):
Thread 0: waiting for lock...
Thread 0: got lock, s is {1, 2, 3}
Thread 1: waiting for lock...
Thread 0: set s to {5, 6, 7}
Thread 0: releasing lock...
Thread 1: got lock, s is {5, 6, 7}
Thread 1: set s to {10, 11, 12}
Thread 1: releasing lock...