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;
}