欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > linux的线程同步(条件变量和锁)

linux的线程同步(条件变量和锁)

2025/2/24 22:46:26 来源:https://blog.csdn.net/qq_38072731/article/details/144908700  浏览:    关键词:linux的线程同步(条件变量和锁)

目录

一、前言

二、互斥锁(Mutex Lock)

1.互斥锁的原理

2.代码示例(使用 C 语言和 pthread 库)

三、条件变量

1.条件变量的原理

2.代码示例(使用 C 语言和 pthread 库)

四、自旋锁(Spin Lock)

1.自旋锁原理

2.代码示例(使用 C 语言)

五、读写锁(Read - Write Lock)

1.读写锁原理

2.代码示例(使用 C 语言和 pthread 库)

六、总结


一、前言

在Linux系统中,多线程编程是实现高效并发的重要手段。线程同步机制是确保线程间协调工作、避免资源竞争的关键技术。互斥锁、条件变量、自旋锁以及读写锁是常见的线程同步工具,它们在不同场景下发挥着重要作用。

二、互斥锁(Mutex Lock)

1.互斥锁的原理

互斥锁用于保护共享资源,确保在任何时刻只有一个线程可以访问被保护的资源。当一个线程获取了互斥锁后,其他试图获取该锁的线程将被阻塞,直到锁被释放。这种机制通过互斥的方式来避免多个线程同时访问和修改共享数据,从而防止数据竞争和不一致性。

2.代码示例(使用 C 语言和 pthread 库)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 定义互斥锁
pthread_mutex_t mutex;
// 共享变量
int shared_variable = 0;// 线程函数
void *thread_function(void *arg) {// 加锁pthread_mutex_lock(&mutex);// 对共享变量进行操作shared_variable++;printf("Thread ID: %lu, Shared variable value: %d\n", pthread_self(), shared_variable);// 解锁pthread_mutex_unlock(&mutex);pthread_exit(NULL);
}int main() {pthread_t threads[5];// 初始化互斥锁if (pthread_mutex_init(&mutex, NULL)!= 0) {perror("Mutex initialization failed");return 1;}for (int i = 0; i < 5; ++i) {// 创建线程if (pthread_create(&threads[i], NULL, thread_function, NULL)!= 0) {perror("Thread creation failed");return 1;}}for (int i = 0; i < 5; ++i) {// 等待线程结束if (pthread_join(threads[i], NULL)!= 0) {perror("Thread join failed");return 1;}}// 销毁互斥锁if (pthread_mutex_destroy(&mutex)!= 0) {perror("Mutex destruction failed");return 1;}return 0;
}
  • 分析
    • 首先,通过pthread_mutex_init函数初始化互斥锁mutex。这个函数在成功时返回 0,否则返回一个错误码。
    • thread_function函数中,每个线程首先调用pthread_mutex_lock来获取互斥锁。如果此时锁已经被其他线程持有,调用线程会被阻塞在这里,直到锁被释放。
    • 当线程成功获取锁后,它对共享变量shared_variable进行自增操作,并打印出线程 ID 和共享变量的值。然后,通过pthread_mutex_unlock释放互斥锁,使得其他线程有机会获取锁并访问共享资源。
    • main函数中,创建了 5 个线程,并使用pthread_join等待每个线程结束。最后,通过pthread_mutex_destroy销毁互斥锁。

三、条件变量

1.条件变量的原理

条件变量通常与互斥锁一起使用,用于线程之间的同步。它允许一个线程等待某个条件为真,而另一个线程可以在条件满足时通知等待的线程。这样可以避免线程在不满足条件的情况下一直占用 CPU 资源进行无效的检查。

2.代码示例(使用 C 语言和 pthread 库)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 定义互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
// 共享变量
int shared_variable = 0;// 生产者线程函数
void *producer_thread(void *arg) {pthread_mutex_lock(&mutex);// 生产数据,修改共享变量shared_variable++;// 发送信号给等待的消费者线程pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);pthread_exit(NULL);
}// 消费者线程函数
void *consumer_thread(void *arg) {pthread_mutex_lock(&mutex);// 检查条件是否满足,如果不满足则等待while (shared_variable == 0) {pthread_cond_wait(&cond, &mutex);}// 消费数据,访问共享变量printf("Consumer: Shared variable value: %d\n", shared_variable);pthread_mutex_unlock(&mutex);pthread_exit(NULL);
}int main() {pthread_t producer, consumer;// 初始化互斥锁和条件变量if (pthread_mutex_init(&mutex, NULL)!= 0) {perror("Mutex initialization failed");return 1;}if (pthread_cond_init(&cond, NULL)!= 0) {perror("Condition variable initialization failed");return 1;}// 创建生产者线程if (pthread_create(&producer, NULL, producer_thread, NULL)!= 0) {perror("Producer thread creation failed");return 1;}// 创建消费者线程if (pthread_create(&consumer, NULL, consumer_thread, NULL)!= 0) {perror("Consumer thread creation failed");return 1;}// 等待生产者和消费者线程结束if (pthread_join(producer, NULL)!= 0) {perror("Producer thread join failed");return 1;}if (pthread_join(consumer, NULL)!= 0) {perror("Consumer thread join failed");return 1;}// 销毁互斥锁和条件变量if (pthread_mutex_destroy(&mutex)!= 0) {perror("Mutex destruction failed");return 1;}if (pthread_cond_destroy(&cond)!= 0) {perror("Condition variable destruction failed");return 1;}return 0;
}
  • 分析
    • main函数中,首先通过pthread_mutex_initpthread_cond_init分别初始化互斥锁mutex和条件变量cond
    • 生产者线程函数producer_thread获取互斥锁,修改共享变量shared_variable,然后通过pthread_cond_signal发送信号,表示共享变量已经被修改,可能满足消费者线程等待的条件。最后释放互斥锁。
    • 消费者线程函数consumer_thread获取互斥锁后,检查共享变量是否满足条件(这里是shared_variable是否大于 0)。如果不满足条件,就调用pthread_cond_wait函数等待。这个函数会自动释放互斥锁,让其他线程(如生产者线程)能够获取锁并修改共享变量。当收到信号并且条件满足时,pthread_cond_wait会重新获取互斥锁,然后消费者线程继续执行,访问共享变量并打印相关信息,最后释放互斥锁。
    • main函数最后,销毁互斥锁和条件变量。

四、自旋锁(Spin Lock)

1.自旋锁原理

自旋锁也是一种用于保护共享资源的同步机制。当一个线程试图获取自旋锁而锁已经被占用时,它不会像互斥锁那样阻塞自己,而是会一直 “自旋”,即不断地检查锁是否已经被释放。这种方式在等待时间较短的情况下可能比互斥锁更高效,因为它避免了线程切换的开销。但是,如果等待时间过长,自旋锁会占用大量的 CPU 资源,因为线程一直在循环检查。

2.代码示例(使用 C 语言)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>// 定义自旋锁结构体
typedef struct {volatile bool locked;
} spinlock_t;// 初始化自旋锁
void spinlock_init(spinlock_t *lock) {lock->locked = false;
}// 获取自旋锁
void spinlock_lock(spinlock_t *lock) {while (__sync_lock_test_and_set(&lock->locked, true)) {// 自旋等待}
}// 释放自旋锁
void spinlock_unlock(spinlock_t *lock) {__sync_lock_release(&lock->locked);
}// 共享变量
int shared_variable = 0;
spinlock_t spinlock;// 线程函数
void *thread_function(void *arg) {spinlock_lock(&spinlock);shared_variable++;printf("Thread ID: %lu, Shared variable value: %d\n", pthread_self(), shared_variable);spinlock_unlock(&spinlock);pthread_exit(NULL);
}int main() {pthread_t threads[5];spinlock_init(&spinlock);for (int i = 0; i < 5; ++i) {if (pthread_create(&threads[i], NULL, thread_function, NULL)!= 0) {perror("Thread creation failed");return 1;}}for (int i = 0; i < 5; ++i) {if (pthread_join(threads[i], NULL)!= 0) {perror("Thread join failed");return 1;}}return 0;
}
  • 分析
    • 首先定义了自旋锁结构体spinlock_t,其中包含一个volatile bool类型的变量locked用于表示锁的状态。volatile关键字用于告诉编译器这个变量可能会被外部因素(如其他线程)改变,不要对其进行优化。
    • spinlock_init函数用于初始化自旋锁,将locked设置为false,表示锁未被占用。
    • spinlock_lock函数通过__sync_lock_test_and_set原子操作来尝试获取自旋锁。这个原子操作会检查locked的值,如果为false,则将其设置为true并返回旧值(此时获取锁成功);如果为true,则返回true,表示锁已经被占用,线程会进入循环等待(自旋),直到获取到锁。
    • thread_function函数中,线程首先获取自旋锁,然后对共享变量shared_variable进行操作,打印相关信息,最后释放自旋锁。
    • main函数中,创建了多个线程并等待它们结束。

五、读写锁(Read - Write Lock)

1.读写锁原理

读写锁用于区分对共享资源的读操作和写操作。它允许多个线程同时对共享资源进行读操作,但在有线程进行写操作时,其他线程(无论是读还是写)都需要等待。这种机制适用于共享资源读操作频繁而写操作相对较少的场景,可以提高并发性能。

2.代码示例(使用 C 语言和 pthread 库)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>// 定义读写锁
pthread_rwlock_t rwlock;
// 共享变量
int shared_variable = 0;// 读线程函数
void *read_thread(void *arg) {// 获取读锁pthread_rwlock_rdlock(&rwlock);printf("Read thread ID: %lu, Shared variable value: %d\n", pthread_self(), shared_variable);// 释放读锁pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}// 写线程函数
void *write_thread(void *arg) {// 获取写锁pthread_rwlock_wrlock(&rwlock);shared_variable++;printf("Write thread ID: %lu, Shared variable value: %d\n", pthread_self(), shared_variable);// 释放写锁pthread_rwlock_unlock(&rwlock);pthread_exit(NULL);
}int main() {pthread_t read_threads[5];pthread_t write_thread;// 初始化读写锁if (pthread_rwlock_init(&rwlock, NULL)!= 0) {perror("Read - write lock initialization failed");return 1;}for (int i = 0; i < 5; ++i) {// 创建读线程if (pthread_create(&read_threads[i], NULL, read_thread, NULL)!= 0) {perror("Read thread creation failed");return 1;}}// 创建写线程if (pthread_create(&write_thread, NULL, write_thread, NULL)!= 0) {perror("Write thread creation failed");return 1;}for (int i = 0; i < 5; ++i) {// 等待读线程结束if (pthread_join(read_threads[i], NULL)!= 0) {perror("Read thread join failed");return 1;}}if (pthread_join(write_thread, NULL)!= 0) {perror("Write thread join failed");return 1;}// 销毁读写锁if (pthread_rwlock_destroy(&rwlock)!= 0) {perror("Read - write lock destruction failed");return 1;}return 0;
}
  • 分析
    • main函数中,首先通过pthread_rwlock_init函数初始化读写锁rwlock
    • 读线程函数read_thread通过pthread_rwlock_rdlock获取读锁,这样多个读线程可以同时获取读锁并访问共享变量shared_variable。读取完成后,通过pthread_rwlock_unlock释放读锁。
    • 写线程函数write_thread通过pthread_rwlock_wrlock获取写锁。当一个线程获取写锁后,其他线程(无论是读还是写)都无法获取锁,直到写操作完成。写线程对共享变量进行自增操作,打印相关信息,然后释放写锁。
    • main函数最后,等待所有线程结束并销毁读写锁。

六、总结

互斥锁、条件变量、自旋锁和读写锁在Linux线程同步中各有特点和适用场景。互斥锁是最常用的同步工具,适用于大多数场景。条件变量用于在特定条件满足时通知线程。自旋锁适用于对锁的等待时间较短且不希望线程上下文切换的场景。读写锁则在对共享资源进行读和写操作时提高效率。

 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词