在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。C语言提供了多种同步机制,以下将详细介绍五种实用的线程同步技巧,并结合具体案例进行分析。
1. 使用互斥锁(Mutex)
互斥锁是同步线程最常用的工具之一,它确保一次只有一个线程可以访问共享资源。
案例分析
假设有一个全局计数器counter,多个线程需要对其进行递增操作。
#include <pthread.h>
pthread_mutex_t lock;
int counter = 0;
void* increment(void* arg) {
for (int i = 0; i < 1000; i++) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main() {
pthread_t threads[10];
pthread_mutex_init(&lock, NULL);
for (int i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, increment, NULL);
}
for (int i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
pthread_mutex_destroy(&lock);
printf("Final counter value: %d\n", counter);
return 0;
}
在这个案例中,互斥锁lock确保了counter的递增操作是线程安全的。
2. 条件变量(Condition Variable)
条件变量允许线程在某个条件不满足时挂起,直到其他线程修改了共享资源并发出信号。
案例分析
一个生产者-消费者问题,生产者生产数据,消费者消费数据。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
pthread_mutex_t lock;
pthread_cond_t not_empty, not_full;
void* producer(void* arg) {
while (1) {
pthread_mutex_lock(&lock);
while (in == out) {
pthread_cond_wait(¬_empty, &lock);
}
buffer[in] = rand() % 100;
in = (in + 1) % BUFFER_SIZE;
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&lock);
sleep(1);
}
return NULL;
}
void* consumer(void* arg) {
while (1) {
pthread_mutex_lock(&lock);
while (in == out) {
pthread_cond_wait(¬_full, &lock);
}
int item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
printf("Consumed: %d\n", item);
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&lock);
sleep(2);
}
return NULL;
}
int main() {
pthread_t prod, cons;
pthread_mutex_init(&lock, NULL);
pthread_cond_init(¬_empty, NULL);
pthread_cond_init(¬_full, NULL);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(prod, NULL);
pthread_join(cons, NULL);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(¬_empty);
pthread_cond_destroy(¬_full);
return 0;
}
在这个案例中,生产者和消费者使用条件变量来同步对缓冲区的访问。
3. 读写锁(Read-Write Lock)
读写锁允许多个线程同时读取共享资源,但写入时必须独占访问。
案例分析
一个日志记录系统,多个线程需要写入日志,同时其他线程可以读取日志。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_rwlock_t rwlock;
char* log = NULL;
void* writer(void* arg) {
char* message = (char*)arg;
pthread_rwlock_wrlock(&rwlock);
if (log == NULL) {
log = malloc(100);
if (log == NULL) {
perror("Failed to allocate memory for log");
exit(EXIT_FAILURE);
}
}
snprintf(log, 100, "%s\n", message);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
void* reader(void* arg) {
pthread_rwlock_rdlock(&rwlock);
if (log != NULL) {
printf("Log: %s\n", log);
}
pthread_rwlock_unlock(&rwlock);
return NULL;
}
int main() {
pthread_t writers[10], readers[10];
pthread_rwlock_init(&rwlock, NULL);
for (int i = 0; i < 10; i++) {
char* messages[] = {"Write message 1", "Write message 2", "Write message 3"};
pthread_create(&writers[i], NULL, writer, messages[i]);
pthread_create(&readers[i], NULL, reader, NULL);
}
for (int i = 0; i < 10; i++) {
pthread_join(writers[i], NULL);
pthread_join(readers[i], NULL);
}
pthread_rwlock_destroy(&rwlock);
free(log);
return 0;
}
在这个案例中,读写锁确保了日志写入的线程安全,并允许多个线程同时读取日志。
4. 信号量(Semaphore)
信号量是一种更通用的同步机制,可以用于多种同步场景。
案例分析
一个停车场模型,最多只能有5辆车同时停车。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_sem_t sem;
int cars = 0;
void* car(void* arg) {
pthread_sem_wait(&sem);
cars++;
printf("Car %d parked\n", (int)arg);
pthread_sem_post(&sem);
sleep(1);
pthread_sem_wait(&sem);
cars--;
printf("Car %d leaving\n", (int)arg);
pthread_sem_post(&sem);
return NULL;
}
int main() {
pthread_sem_init(&sem, 0, 5);
pthread_t threads[10];
for (int i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, car, (void*)i);
}
for (int i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
pthread_sem_destroy(&sem);
return 0;
}
在这个案例中,信号量sem确保了最多只有5辆车可以同时停在停车场。
5. 原子操作(Atomic Operations)
原子操作用于在多线程环境中执行不可分割的操作,防止数据竞争。
案例分析
一个简单的原子递增操作。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int counter = 0;
pthread_mutex_t lock;
void* increment(void* arg) {
for (int i = 0; i < 1000; i++) {
__atomic_add_fetch(&counter, 1, __ATOMIC_SEQ_CST);
}
return NULL;
}
int main() {
pthread_t threads[10];
for (int i = 0; i < 10; i++) {
pthread_create(&threads[i], NULL, increment, NULL);
}
for (int i = 0; i < 10; i++) {
pthread_join(threads[i], NULL);
}
printf("Final counter value: %d\n", counter);
return 0;
}
在这个案例中,__atomic_add_fetch函数用于原子地递增counter。
通过掌握这些线程同步技巧,你可以在C语言中更有效地编写多线程程序,确保程序的稳定性和正确性。
