1、C语言中的原子性
原子操作是指该操作作为一个单一的、不被中断的步骤执行。在多线程环境中,原子操作保证操作完成时不被其他线程干扰,从而避免了竞争条件。C 语言中,i++
操作默认不是原子操作。在单线程环境中,由于没有其他线程修改变量,因此没有原子性的问题。在多线程程序中,当多个线程同时对 i
进行自增操作时,除非使用同步机制(如互斥锁或 C11 中提供的原子操作),否则可能会出现竞争条件。
#include <stdio.h> #include <stdatomic.h> #include <pthread.h> atomic_int shared_counter = 0; // 原子类型,确保线程安全 void* increment(void* arg) { for (int i = 0; i < 100000; i++) { atomic_fetch_add(&shared_counter, 1); // 原子增加 } return NULL; } int main() { pthread_t thread1, thread2; // 创建两个线程 pthread_create(&thread1, NULL, increment, NULL); pthread_create(&thread2, NULL, increment, NULL); // 等待线程完成 pthread_join(thread1, NULL); pthread_join(thread2, NULL); printf("Final counter value: %d\n", shared_counter); return 0; }
2、C11 中的 stdatomic.h
为了执行原子操作,可以使用 C11 的 库,它提供了原子类型和操作,确保线程安全。
#include <stdio.h> #include <stdatomic.h> int main() { atomic_int i = 0; // 原子自增 atomic_fetch_add(&i, 1); printf("i: %d\n", i); return 0; }
没有原子操作时,在多线程程序中自增操作可能会遇到竞争条件:
#include <stdio.h> #include <pthread.h> int i = 0; void *increment(void *arg) { for (int j = 0; j < 100000; j++) { i++; // 不是原子操作,可能会导致竞争条件 } return NULL; } int main() { pthread_t t1, t2; pthread_create(&t1, NULL, increment, NULL); pthread_create(&t2, NULL, increment, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("Final value of i: %d\n", i); return 0; }
3、使用互斥锁的解决方案
为了避免竞争条件,可以使用互斥锁来同步对变量的访问,pthread_mutex_lock
和 pthread_mutex_unlock
保证每次只有一个线程可以访问i
,从而避免了竞争条件。
#include <stdio.h> #include <pthread.h> int i = 0; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *increment(void *arg) { for (int j = 0; j < 100000; j++) { pthread_mutex_lock(&lock); i++; pthread_mutex_unlock(&lock); } return NULL; } int main() { pthread_t t1, t2; pthread_create(&t1, NULL, increment, NULL); pthread_create(&t2, NULL, increment, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf("Final value of i: %d\n", i); return 0; }