互斥锁和条件变量总结&接口介绍【pthread_mutex_init/lock/trylock/unlock/destroy】【pthread_cond_init/wait/timedwait/..】

本文详细介绍了互斥锁和条件变量的概念及其关键接口,包括初始化、加锁/解锁、等待及销毁等操作,并解释了它们在多线程环境中的作用。

0 - 前言

本文主要总结互斥锁和条件变量的关键接口

参考:

C语言技术网-线程同步

条件变量

1 - 互斥锁

互斥锁时对于互斥资源(也叫临界资源)来说的,比如说有一个公共账户,有很多人都想对这个账户进行操作(存钱、取钱),为了保护账户操作的稳定,使用一种保障机制来保证同一时刻只有一个人操作账户。这种保障机制就是互斥锁。

每个临界资源对应一个互斥锁,多个线程都想获取临界资源的使用权时,必须先获得互斥锁,锁定临界资源后,才能对临界资源进行操作。

互斥锁的类型为:pthread_mutex_t

1-1 初始化锁

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);

1-2 加锁

加锁分为阻塞加锁非阻塞加锁两种

阻塞加锁

int pthread_mutex_lock(pthread_mutex *mutex);

如果是锁是空闲状态,本线程将获得这个锁;如果锁已经被占据,本线程将排队等待,直到成功的获取锁。

非阻塞加锁

int pthread_mutex_trylock( pthread_mutex_t *mutex);

该函数语义与 pthread_mutex_lock() 类似,不同的是在锁已经被占据时立即返回 EBUSY(一种错误类型),而不是挂起等待。

1-3 解锁

int pthread_mutex_unlock(pthread_mutex *mutex);

1-4 销毁锁

int pthread_mutex_destroy(pthread_mutex *mutex);

销毁锁之前,锁必需是空闲状态(unlock),否则返回EBUSY

2 - 条件变量

条件变量是用来等待线程而不是上锁的,条件变量通常和互斥锁一起使用。条件变量之所以要和互斥锁一起使用,主要是因为互斥锁的一个明显的特点就是它只有两种状态:锁定和非锁定,而条件变量可以通过允许线程阻塞和等待另一个线程发送信号来弥补互斥锁的不足,所以互斥锁和条件变量通常一起使用

条件变量的类型为:pthread_cond_t

2-1 初始化条件变量

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)

2-2 等待条件变量

分为阻塞等待与**非阻塞等待(也叫限时等待)**两种

阻塞等待

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或 pthread_cond_broadcast来唤醒它 。

pthread_cond_wait() 必须与pthread_mutex 配套使用。 pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。

也就是说,pthread_cond_wait函数成功返回时,内部完成了三件事

1、释放互斥锁:刚调用该函数,使线程进入等待

2、等待条件变量:线程开始等待。当另一个线程改变条件变量,满足触发条件时,正在等待的这个线程进入第3步

3、条件触发,互斥锁加锁:本线程对临界资源加锁,开始自己的操作(等了这么久,终于轮到我对临界资源操作了)

限时等待

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)

2-3 唤醒线程

唤醒阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond);

pthread_cond_signal 函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回

int pthread_cond_signal(pthread_cond_t *cond); 

pthread_cond_broadcast() 唤醒全部阻塞在条件变量上的线程

2-4 销毁条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

只有在没有线程在该条件变量上等待时,才可以注销条件变量,否则会返回EBUSY

个 #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0; int out = 0; sem_t empty; sem_t full; pthread_mutex_t mutex; // 生产者线程函数 void *producer(void *arg) { int item; for (int i = 0; i < 10; i++) { item = i; sem_wait(&empty); // 等待缓冲区有空位 pthread_mutex_lock(&mutex); // 进入临界区 buffer[in] = item; printf("Produced %d at position %d\n", item, in); in = (in + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); // 离开临界区 sem_post(&full); // 通知缓冲区有新数据 } pthread_exit(NULL); } // 消费者线程函数 void *consumer(void *arg) { int item; for (int i = 0; i < 10; i++) { sem_wait(&full); // 等待缓冲区有数据 pthread_mutex_lock(&mutex); // 进入临界区 item = buffer[out]; printf("Consumed %d from position %d\n", item, out); out = (out + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); // 离开临界区 sem_post(&empty); // 通知缓冲区有空位 } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; // 初始化信号量互斥锁 sem_init(&empty, 0, BUFFER_SIZE); sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL); // 创建生产者消费者线程 pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // 等待线程结束 pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); // 销毁信号量互斥锁 sem_destroy(&empty); sem_destroy(&full); pthread_mutex_destroy(&mutex); return 0; } 第二个 #include <stdio.h> #include <pthread.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0; int out = 0; int count = 0; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty; // 生产者线程函数 void *producer(void *arg) { int item; for (int i = 0; i < 10; i++) { item = i; pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) { pthread_cond_wait(&not_full, &mutex); // 等待缓冲区有空位 } buffer[in] = item; printf("Produced %d at position %d\n", item, in); in = (in + 1) % BUFFER_SIZE; count++; pthread_cond_signal(&not_empty); // 通知缓冲区有新数据 pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } // 消费者线程函数 void *consumer(void *arg) { int item; for (int i = 0; i < 10; i++) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(&not_empty, &mutex); // 等待缓冲区有数据 } item = buffer[out]; printf("Consumed %d from position %d\n", item, out); out = (out + 1) % BUFFER_SIZE; count--; pthread_cond_signal(&not_full); // 通知缓冲区有空位 pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; // 初始化互斥锁条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&not_full, NULL); pthread_cond_init(&not_empty, NULL); // 创建生产者消费者线程 pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // 等待线程结束 pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); // 销毁互斥锁条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&not_full); pthread_cond_destroy(&not_empty); return 0; } 第三个 #include <stdio.h> #include <pthread.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0; int out = 0; pthread_mutex_t mutex; // 生产者线程函数 void *producer(void *arg) { int item; for (int i = 0; i < 10; i++) { item = i; pthread_mutex_lock(&mutex); while ((in + 1) % BUFFER_SIZE == out) { // 缓冲区满,等待 } buffer[in] = item; printf("Produced %d at position %d\n", item, in); in = (in + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } // 消费者线程函数 void *consumer(void *arg) { int item; for (int i = 0; i < 10; i++) { pthread_mutex_lock(&mutex); while (in == out) { // 缓冲区空,等待 } item = buffer[out]; printf("Consumed %d from position %d\n", item, out); out = (out + 1) % BUFFER_SIZE; pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; // 初始化互斥锁 pthread_mutex_init(&mutex, NULL); // 创建生产者消费者线程 pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // 等待线程结束 pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); // 销毁互斥锁 pthread_mutex_destroy(&mutex); return 0; } 第四个 #include <stdio.h> #include <pthread.h> #include <semaphore.h> #define N 5 sem_t chopsticks[N]; // 哲学家线程函数 void *philosopher(void *num) { int id = *(int *)num; int left = id; int right = (id + 1) % N; while (1) { // 思考 printf("Philosopher %d is thinking.\n", id); // 尝试拿起筷子 sem_wait(&chopsticks[left]); printf("Philosopher %d picked up left chopstick.\n", id); sem_wait(&chopsticks[right]); printf("Philosopher %d picked up right chopstick.\n", id); // 吃饭 printf("Philosopher %d is eating.\n", id); // 放下筷子 sem_post(&chopsticks[right]); printf("Philosopher %d put down right chopstick.\n", id); sem_post(&chopsticks[left]); printf("Philosopher %d put down left chopstick.\n", id); } pthread_exit(NULL); } int main() { pthread_t threads[N]; int ids[N]; // 初始化信号量 for (int i = 0; i < N; i++) { sem_init(&chopsticks[i], 0, 1); } // 创建哲学家线程 for (int i = 0; i < N; i++) { ids[i] = i; pthread_create(&threads[i], NULL, philosopher, &ids[i]); } // 等待线程结束 for (int i = 0; i < N; i++) { pthread_join(threads[i], NULL); } // 销毁信号量 for (int i = 0; i < N; i++) { sem_destroy(&chopsticks[i]); } return 0; } 第五个 #include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> // 用于usleep减少CPU占用 #define N 5 // 哲学家数量 #define MAX_MEALS 3 // 每个哲学家最大进食次数 #define THINK_TIME 1 // 思考时间(秒) #define EAT_TIME 1 // 进食时间(秒) sem_t chopsticks[N]; int eat_count[N] = {0}; // 记录各哲学家进食次数 // 哲学家线程函数 void *philosopher(void *num) { int id = *(int *)num; int left = id; int right = (id + 1) % N; while (eat_count[id] < MAX_MEALS) { // 思考阶段 printf("Philosopher %d is thinking.\n", id); usleep(THINK_TIME * 1000000); // 转换为微秒 // 交替获取筷子策略 if (id % 2 == 0) { sem_wait(&chopsticks[left]); sem_wait(&chopsticks[right]); } else { sem_wait(&chopsticks[right]); sem_wait(&chopsticks[left]); } // 进食阶段 printf("Philosopher %d is eating (meal %d).\n", id, eat_count[id] + 1); usleep(EAT_TIME * 1000000); eat_count[id]++; // 释放筷子 sem_post(&chopsticks[left]); sem_post(&chopsticks[right]); printf("Philosopher %d released chopsticks.\n", id); } printf("Philosopher %d has finished eating.\n", id); pthread_exit(NULL); } int main() { pthread_t threads[N]; int ids[N]; // 初始化信号量进食计数器 for (int i = 0; i < N; i++) { sem_init(&chopsticks[i], 0, 1); eat_count[i] = 0; } // 创建哲学家线程 for (int i = 0; i < N; i++) { ids[i] = i; if (pthread_create(&threads[i], NULL, philosopher, &ids[i]) != 0) { perror("Failed to create thread"); return 1; } } // 等待所有线程结束 for (int i = 0; i < N; i++) { pthread_join(threads[i], NULL); } // 销毁信号量 for (int i = 0; i < N; i++) { sem_destroy(&chopsticks[i]); } printf("All philosophers have finished dining.\n"); return 0; } 第六个 #include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define N 5 // 哲学家数量 #define MAX_MEALS 3 // 每个哲学家最大进食次数 #define THINK_TIME 1 // 思考时间(秒) #define EAT_TIME 1 // 进食时间(秒) sem_t chopsticks[N]; sem_t room; int meals_eaten[N] = {0}; // 记录各哲学家进食次数 // 哲学家线程函数 void *philosopher(void *num) { int id = *(int *)num; int left = id; int right = (id + 1) % N; while (meals_eaten[id] < MAX_MEALS) { // 思考阶段 printf("Philosopher %d is thinking (meal %d/%d).\n", id, meals_eaten[id], MAX_MEALS); usleep(THINK_TIME * 1000000); // 进入房间(限制最多N-1人同时进餐) sem_wait(&room); printf("Philosopher %d entered the room.\n", id); // 拿起筷子 sem_wait(&chopsticks[left]); sem_wait(&chopsticks[right]); printf("Philosopher %d picked up chopsticks %d and %d.\n", id, left, right); // 进食阶段 printf("Philosopher %d is eating (meal %d/%d).\n", id, meals_eaten[id] + 1, MAX_MEALS); usleep(EAT_TIME * 1000000); meals_eaten[id]++; // 放下筷子 sem_post(&chopsticks[left]); sem_post(&chopsticks[right]); printf("Philosopher %d released chopsticks %d and %d.\n", id, left, right); // 离开房间 printf("Philosopher left the room.\n"); printf("\n"); // 分开两个换行 sem_post(&room); } printf("Philosopher %d has finished dining (ate %d meals).\n", id, MAX_MEALS); pthread_exit(NULL); } int main() { pthread_t threads[N]; int ids[N]; // 初始化信号量 for (int i = 0; i < N; i++) { sem_init(&chopsticks[i], 0, 1); } sem_init(&room, 0, N-1); // 允许最多N-1个哲学家同时进餐 // 创建哲学家线程 for (int i = 0; i < N; i++) { ids[i] = i; if (pthread_create(&threads[i], NULL, philosopher, &ids[i]) != 0) { perror("Failed to create thread"); return 1; } } // 等待所有线程结束 for (int i = 0; i < N; i++) { pthread_join(threads[i], NULL); } // 销毁信号量 for (int i = 0; i < N; i++) { sem_destroy(&chopsticks[i]); } sem_destroy(&room); printf("All philosophers have completed dining.\n"); return 0; }以上代码的思路详细代码分析让初学者能看明白了解给我
最新发布
10-29
将原始 Windows 代码重构为跨平台实现,保留 补全 修改 Windows 兼容性同时支持 Linux 编译 #if defined (_MSC_VER) && (_MSC_VER >= 1000) #pragma once #endif #ifndef _HEADER_BASETHREAD #define _HEADER_BASETHREAD #include "Config.h" #include "Semaphore.h" #include <assert.h> //#include "BaseLibary/PoolT.h" #define MUITLEVENT_WAITFOREVER -1 #define MUITLEVENT_WAIT_TIMEOUT 1 #define IS_TIMEOUT(ret) (ret == WAIT_TIMEOUT) /** *线程支撑相关命名空间,包括线程控制,互斥等 */ namespace Thread { typedef void (*FNRunThread)(void*);//返回false则停止运行 /** *基本的线程控制处理 */ class BaseThread; /** *简单的互斥控制 */ class Mutex; //Win32平台 //简单互斥,同一线程也不可重入 class _Foundation_Export_ Mutex { private: #ifdef _DEBUG bool _isLocked; #endif #ifdef _WIN32 mutable volatile long _mutex; #else mutable pthread_mutex_t _mutex; #endif public: Mutex() #ifdef _DEBUG :_isLocked(false) #endif { #ifdef _WIN32 _mutex= 0 ; #else #ifdef NDEBUG int rc = pthread_mutex_init(&_mutex, 0); #else int rc; #if defined(__linux) && !defined(__USE_UNIX98) const pthread_mutexattr_t attr = { PTHREAD_MUTEX_ERRORCHECK_NP }; #else pthread_mutexattr_t attr; rc = pthread_mutexattr_init(&attr); assert(rc == 0); rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); assert(rc == 0); #endif//defined(__linux) && !defined(__USE_UNIX98) rc = pthread_mutex_init(&_mutex, &attr); #if defined(__linux) && !defined(__USE_UNIX98) // Nothing to do #else//(__linux) && !defined(__USE_UNIX98) rc = pthread_mutexattr_destroy(&attr); assert(rc == 0); #endif//defined(__linux) && !defined(__USE_UNIX98) #endif//NDEBUG #endif//_WIN32 } ~Mutex() { #ifdef _WIN32 //do nothing #else int rc = 0; rc = pthread_mutex_destroy(&_mutex); assert(rc == 0); #endif } long Lock(void) { #ifdef _WIN32 while( InterlockedCompareExchange(&_mutex,1,0) != 0) { Sleep( 1 ); } #ifdef _DEBUG if (_isLocked) { //assert(false); } _isLocked=true; #endif return 0; #else return pthread_mutex_lock(&_mutex); #endif } bool TryLock(void) { #ifdef _WIN32 if( InterlockedCompareExchange(&_mutex,1,0) != 0) { return false; } else { #ifdef _DEBUG _isLocked=true; #endif return true; } #else int rc = pthread_mutex_trylock(&_mutex); if(rc != 0 && rc != EBUSY) { //Error } return (rc == 0); #endif } void Unlock(void) { #ifdef _WIN32 InterlockedExchange(&_mutex,0); #else pthread_mutex_unlock(&_mutex); #endif #ifdef _DEBUG _isLocked=false; #endif return; } struct SLock { public: SLock(Mutex& mutex) :_mutex(mutex) { _mutex.Lock(); } ~SLock() { _mutex.Unlock(); } Mutex& _mutex; }; }; #ifdef _WIN32 /** *多线程安全的数字 */ class _Foundation_Export_ InterlockedNumber { public: InterlockedNumber(void) :_number(0) { } InterlockedNumber(long i) :_number(0) { InterlockedExchange(&_number,i); } void operator ++(int) { InterlockedIncrement(&_number); } void operator --(int) { InterlockedDecrement(&_number); } operator long () { return _number; } void Add(long value) { InterlockedExchangeAdd(&_number,value); } const InterlockedNumber& operator = (const InterlockedNumber & rhs) { InterlockedExchange(&_number,rhs._number); return *this; } private: volatile long _number; }; #else /** *多线程安全的数字 */ class InterlockedNumber { public: InterlockedNumber(void) :_number(0) { } operator ++() { _number++; } operator --() { _number--; } operator long () { return _number; } private: volatile long _number; }; #endif//WIN32 /** *基本线程处理 */ class _Foundation_Export_ BaseThread { public: BaseThread(void); ~BaseThread(void); BaseThread& operator = (const BaseThread& rhs); //!启动线程 void StartThread(FNRunThread,void*); //!等待线程结束 bool WaitForEnd(void); //!挂起线程,只能在线程函数中调用 void Suspend(void); //!恢复线程,可以在外部调用 void Resume(void); //!强制结束线程 void ForceKill(void); private: #ifdef _WIN32 typedef void* ThreadSysHandle; #else typedef pthread_t ThreadSysHandle; #endif struct ThreadData { ThreadData() :_end(false) ,_param(0) ,_pfnThread(0) {} bool _end; void* _param; FNRunThread _pfnThread; ThreadSysHandle _h; }; ThreadData* _data; //!模拟挂起所使用的互斥变量 Mutex* _simSuspend; //!释放资源 void SafeRelease(void); //!线程运行函数 #ifdef _WIN32 static DWORD WINAPI ProcessThread(void* param); #else static void* ProcessThread(void* param); #endif }; } #endif//_Header
09-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值