互斥锁和条件变量

互斥锁(mutex)

使用流程

  1. 定义锁
  2. 初始化锁
  3. 加锁
  4. 解锁
  5. 销毁锁

定义锁

// 需要先定义一个pthread_mutex_t类型的锁(eg:)
pthread_mutex_t mLock;

初始化锁

对锁的初始化,有两种方式: (man pthread_mutex_destroy)
// 1, 一种是调用pthread_mutex_init函数
		int pthread_mutex_init(
       		pthread_mutex_t *mutex, // 要初始化的锁
   			const pthread_mutexattr_t *attr // 锁的属性类型
   );

// 2, 另一种是直接将锁变量初始化为PTHREAD_MUTEX_INITIALIZER
		pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;

加锁

使用pthread_mutex_lock加锁: (加锁后进入临界区)
		#include <pthread.h>
		// lock a mutex
		int pthread_mutex_lock(
   			pthread_mutex_t *mutex // 锁
   );

解锁

使用pthread_mutex_unlock把锁置为未锁: (临界区代码执行结束,解锁)
		#include <pthread.h>
		// unlock a mutex
		int pthread_mutex_unlock(
   			pthread_mutex_t *mutex // 锁
   );

销毁锁

pthread_mutex_destroy(&mLock);

demo:对全局变量分别进行加法

#include <my_header.h>
#define times 1000
int global = 0;

void *pthreadFunc(void *arg) {
    pthread_mutex_t mLock = *((pthread_mutex_t *)arg);
    for (int i = 0; i < times; i++) {
        pthread_mutex_lock(&mLock);
        global++;
        pthread_mutex_unlock(&mLock);
    }
    return NULL;
}


int main(int argc, char *argv[])
{                                  
    pthread_mutex_t mLock;
    pthread_mutex_init(&mLock,NULL);
    pthread_t pid;
    int ret = pthread_create(&pid,NULL,pthreadFunc,&mLock);
    THREAD_ERROR_CHECK(ret,"pthread_create");
    for (int i = 0; i < times; i++) {
        pthread_mutex_lock(&mLock);
        global++;
        pthread_mutex_unlock(&mLock);
    }
    pthread_join(pid,NULL);
    printf("global : %d\n",global);
    pthread_mutex_destroy(&mLock);
    
    return 0;
}

image-20240509202700858

常用获取时间函数 gettimeofday

#include <sys/time.h>
// get time
int gettimeofday(
	struct timeval *tv, // 是一个指向timeval结构体的指针,用于存储获取的时间和日期
	struct timezone *tz // 是一个指向timezone结构体的指针,用于指定时区信息,通常设置为NULL。
)
struct timeval{
	time_t tv_sec; // seconds 秒
	suseconds_t tv_usec; // microseconds 微妙
}

demo

#include <my_header.h>

int main(int argc, char *argv[])
{                                  
    struct timeval begin,end;
    gettimeofday(&begin,NULL);
    sleep(10);
    gettimeofday(&end,NULL);
    printf("diff : %ld us\n",(end.tv_sec - begin.tv_sec ) * 1000000 + end.tv_usec - begin.tv_usec);
    return 0;
}

image-20240509204023060

非阻塞锁 pthread_mutex_trylock

#include <pthread.h>
// lock a mutex
int pthread_mutex_trylock(
	pthread_mutex_t *mutex
)
// int返回值, 获取到锁则返回0; 没有获取到锁则返回错误码

死锁问题

#include <my_header.h>

pthread_mutex_t mLock1;
pthread_mutex_t mLock2;

void *func(void *arg) {
    
    pthread_mutex_lock(&mLock2);
    sleep(1);
    pthread_mutex_lock(&mLock1);
    
    printf("This is son pthread\n");
    
    pthread_mutex_unlock(&mLock1);
    pthread_mutex_unlock(&mLock2);
    return NULL;
}

int main(int argc, char *argv[])
{                                  
    pthread_mutex_init(&mLock1,NULL);
    pthread_mutex_init(&mLock2,NULL);
    
    pthread_t pid;
    pthread_create(&pid,NULL,func,NULL);
    
    pthread_mutex_lock(&mLock1);
    sleep(1);
    pthread_mutex_lock(&mLock2);
    printf("This is main\n ");
    
    pthread_mutex_unlock(&mLock1);
    pthread_mutex_unlock(&mLock2);
    
    thread_join(pid, NULL);
    
    pthread_mutex_destroy(&mLock1);
    pthread_mutex_destroy(&mLock2);
    return 0;
}

image-20240509205229584

pthread_mutex_trylock解决死锁demo

#include <my_header.h>

pthread_mutex_t mLock1;
pthread_mutex_t mLock2;

void *func(void *arg) {

    while(1){
        pthread_mutex_lock(&mLock2);
        sleep(1);
        int res_trylock = pthread_mutex_trylock(&mLock1);
        if (res_trylock != 0) {
            pthread_mutex_unlock(&mLock2);
            sleep(1);							// 不加的话很容易导致刚释放mLock2马上子线程又给mLock2加锁(原因为释放的时候时间片还没用掉,就会用来获取锁),从而主线程拿不到mLock2,
            printf("try_lock fail\n");
            continue;
        }
        printf("This is son pthread\n");
        pthread_mutex_unlock(&mLock1);
        pthread_mutex_unlock(&mLock2);
        break;
    }
    return NULL;
}

int main(int argc, char *argv[])
{                                  
    pthread_mutex_init(&mLock1,NULL);
    pthread_mutex_init(&mLock2,NULL);
    pthread_t pid;
    pthread_create(&pid,NULL,func,NULL);
    pthread_mutex_lock(&mLock1);
    sleep(1);
    pthread_mutex_lock(&mLock2);
    printf("This is main\n ");
    pthread_mutex_unlock(&mLock1);
    pthread_mutex_unlock(&mLock2);
    pthread_join(pid,NULL);
    pthread_mutex_destroy(&mLock1);
    pthread_mutex_destroy(&mLock2);
    return 0;
}

不断循环检查锁是否被释放,也是一种自旋锁的思想

 while(1){
        pthread_mutex_lock(&mLock2);
        sleep(1);
        int res_trylock = pthread_mutex_trylock(&mLock1);
        if (res_trylock != 0) {
            pthread_mutex_unlock(&mLock2);
            sleep(1);							// 不加的话很容易导致刚释放mLock2马上子线程又给mLock2加锁(原因为释放的时候时间片还没用掉,就会用来获取锁),从而主线程拿不到mLock2,
            printf("try_lock fail\n");
            continue;
        }
        printf("This is son pthread\n");
        pthread_mutex_unlock(&mLock1);
        pthread_mutex_unlock(&mLock2);
        break;
    }

image-20240509210741015

其他锁(只做了解,知道概念即可)

自旋锁

自旋锁(Spin Lock)是一种用于线程同步的锁机制,它不同于传统的互斥锁,它不会导致线程进入睡眠状态,而是会持续检测锁的释放情况,即“自旋”,直到获取到锁为止(忙等待,消耗cpu资源)

自旋锁(pthread_spinlock_t)和普通互斥锁(pthread_mutex_t)在实际效果上是类似的,因为它们都用于线程同步,确保在多线程环境中对共享资源的安全访问。然而,它们在实现上有一些关键区别:

  1. 等待方式:
    • 自旋锁:线程在尝试获取自旋锁时会一直“忙等待”,即持续检查锁是否可用,直到获取为止。
    • 普通互斥锁:当无法获得锁时,线程会被阻塞挂起,等待锁变为可用状态。
  2. 适用场景:
    • 自旋锁适用于短时间内占用锁的情况:如果拥有锁的线程会快速释放锁,或者在锁被占用时其他线程很快就能继续执行其他工作,使用自旋锁可能效率更高。
    • 普通互斥锁适用于长时间占用锁的情况,或者在等待锁释放时可以执行其他任务:如果对锁的占用时间较长,或线程在等待期间有其他工作可做,使用互斥锁可能更合适。

读写锁

读锁: 它允许多个线程同时以读模式访问共享资源。当一个线程持有读锁时,其他线程可以同时获取读锁来读取数据,但只能有一个线程可以获取写锁来写入数据。

写锁: 它只允许一个线程以写模式访问共享资源。当一个线程持有写锁时,其他线程无法获取读锁或写锁,必须等待该锁被释放后才能访问共享资源。

锁的类型 (我感觉不太重要,用到时候再来看吧,用NULL比较多)

我们在初始化锁的时候, 调用pthread_mutex_init(锁, 类型), 可以给锁设置不同的类型 (可 man pthread_mutexattr_gettype 查看)

PTHREAD_MUTEX_NORMAL		// 普通锁. 
PTHREAD_MUTEX_ERRORCHECK	// 检错锁. 同一线程中, 对某个锁重复上锁, 会返回错误, 去解锁未锁的锁,也会返回错误.是一种锁的错误检查机制
PTHREAD_MUTEX_RECURSIVE		// 递归锁/可重入锁.  一个线程中可对该锁重复上锁, 通过计数标记上锁次数, 每次上锁计数+1,每次解锁技术-1; 当计数为0, 其它线程才能获取该锁.
PTHREAD_MUTEX_DEFAULT		// 默认锁,和普通锁表现等价. 
函数定义

定义锁类型

// 需要先定义一个pthread_mutexattr_t的类型(eg:)
pthread_mutexattr_t mutexattr;

初始化类型

#include <pthread.h>
// initialze the mutex attributes object
int pthread_mutexattr_init(
		pthread_mutexattr_t *attr // 类型变量
)

设置类型

#include <pthread.h>
// set mutex type attribute
int pthread_mutexattr_settype(
		pthread_mutexattr_t *attr,  // 类型变量
	int type					// 锁的具体类型
)

使用

#include <pthread.h>
// 初始化锁的时候, 给锁传入类型
int pthread_mutex_init(
      		pthread_mutex_t *mutex, // 要初始化的锁
  			const pthread_mutexattr_t *attr // 锁的类型
  );

销毁

#include <pthread.h>
// destroy the mutex attributes object
int pthread_mutexattr_destroy(
		pthread_mutexattr_t *attr  // 类型变量
)
代码示例

EgCode: 正常锁

#include <testfun.h>
// 使用正常锁
int main(int argc,char*argv[])
{
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);

  pthread_mutex_t mLock;
  pthread_mutex_init(&mLock, &attr);

  pthread_mutex_lock(&mLock);
  int res_errorcheck_lock =  pthread_mutex_lock(&mLock);
  THREAD_ERROR_CHECK(res_errorcheck_lock, "lock repetition");

  pthread_mutex_unlock(&mLock);

  pthread_mutex_destroy(&mLock);
  pthread_mutexattr_destroy(&attr);
  return 0;
}

EgCode: 检错锁

#include <testfun.h>
// 使用检错锁
int main(int argc,char*argv[])
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);

pthread_mutex_t mLock;
pthread_mutex_init(&mLock, &attr);

pthread_mutex_lock(&mLock);
int res_errorcheck_lock =  pthread_mutex_lock(&mLock);
THREAD_ERROR_CHECK(res_errorcheck_lock, "lock repetition");

pthread_mutex_unlock(&mLock);

pthread_mutex_destroy(&mLock);
pthread_mutexattr_destroy(&attr);
return 0;
}

EgCode: 递归锁/可重复锁

#include <testfun.h>
// 使用重入锁
int main(int argc,char*argv[])
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

pthread_mutex_t mLock;
pthread_mutex_init(&mLock, &attr);

pthread_mutex_lock(&mLock);
int res_errorcheck_lock =  pthread_mutex_lock(&mLock);
THREAD_ERROR_CHECK(res_errorcheck_lock, "lock repetition");

pthread_mutex_unlock(&mLock);
pthread_mutex_unlock(&mLock);

pthread_mutex_destroy(&mLock);
pthread_mutexattr_destroy(&attr);
return 0;
}

检测题目 卖票程序

以一个卖票的逻辑为例:    
// 一个人卖票: 票未必每一次都能卖掉, 每一次买票的人在随机的状态下选择是否买票
// 另一个人加票
// 在初始20张票的情况下:50%概率每次卖一张
// 当第一次票小于5张的时候再追加一次票: 10张票(一共只能加一次哦)
#include <my_header.h>

typedef  struct share_state{
    int flag;   // 1表示加过一次票了,0表示没加过
    int ticketNum;
    pthread_mutex_t mLock;
}share_state_t;

void *sell(void *arg) {
    // 注意这里一定要用指针,不要用结构体,不然会导致和sell用的不是同一个结构体
    share_state_t *shareState = (share_state_t *) arg;
    while(1) {
        pthread_mutex_lock(&shareState->mLock);
        // 判断是否小于等于0张,若小于等于0且已经加过票了,那就释放锁退出循环
        if (shareState->ticketNum <= 0 && shareState->flag == 1) {
            pthread_mutex_unlock(&shareState->mLock);
            break;
        }
        // 没有加过票,那么若小于等于0张,则不卖,释放锁,到下一个循环
        // 大于0张那么就卖票
        struct timeval now;
        gettimeofday(&now,NULL);
        srand(now.tv_usec);
        double rate = rand() * 1.0 / RAND_MAX;
        if (rate <= 0.5 && shareState->ticketNum > 0) {
            shareState->ticketNum--;
            printf("sell a ticket now ticketNum : %d\n",shareState->ticketNum);
        }
        pthread_mutex_unlock(&shareState->mLock);
        sleep(1);
    }
    return NULL;
}

void *add(void *arg) {
    share_state_t *shareState = (share_state_t *) arg;
     while(1) {
         pthread_mutex_lock(&shareState->mLock);
         if (shareState->ticketNum <= 5) {
             // printf("add yes\n");
             shareState->ticketNum += 10;
             shareState->flag = 1;
             printf("add tickets 10,now ticketNum : %d\n",shareState->ticketNum);
             pthread_mutex_unlock(&shareState->mLock);
             break;
         }
         pthread_mutex_unlock(&shareState->mLock);
         sleep(1);
     }
    return NULL;
}


int main(int argc, char *argv[])
{                    
    share_state_t shareState;
    shareState.flag = 0;
    shareState.ticketNum = 20;
    pthread_mutex_init(&shareState.mLock,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,sell,&shareState);
    pthread_create(&pid2,NULL,add,&shareState);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    return 0;
}

image-20240509221853343

条件变量

问题

我们发现上述写代码的时候,比如

image-20240509222223174

我们会用sleep函数来避免释放锁后线程马上获得锁,从而导致一直在一个线程循环的情况,但是用sleep函数来避免这种问题是一种合适的方式吗,不是的,我们可以用条件变量来解决这种“加锁-检查条件不满足-解锁”的行为

原理

线程可以在不满足共享资源的某个条件时等待/挂起,直到另一个线程发出通知,告诉它条件已经满足, 进一步唤醒这个等待。
Eg:
当A线程持有锁的时候,A认为自己做某些操作的条件还不够成熟,A可以主动让自己阻塞并且释放锁(陷入阻塞和解锁是原子的)
当锁被A释放,其他的线程比如B线程可以持有锁(有些情况下可以不持有锁)去修改条件的内容
B进行逻辑操作的过程中, 一旦B认为现在是一个合适的时机唤醒A时,B可以通过唤醒操作通知到A线程。
A线程收到唤醒通知之后,会首先恢复运行并加锁,再继续执行后续的指令。
这其中涉及到两个动作: A主动阻塞, B唤醒A

pthread_cond

我们可以在线程运行过程中, 通过调用pthread_cond_wait让不满足条件的线程主动阻塞, 等待被唤醒.

当通过pthread_cond_wait陷入阻塞的时候, 会先释放锁.

pthread_cond_wait被从阻塞状态唤醒的时候, 会先加锁, 然后继续执行其后的代码逻辑.

使用方法

定义条件变量

pthread_cond_t cond;

初始化条件变量

#include <pthread.h>
// initialize condition variables 
int pthread_cond_init(
		pthread_cond_t *cond,		// 条件变量的指针
	pthread_condattr_t *attr	// 条件变量属性对象 (默认NULL)
)// 返回值: 初始化成功返回0;否则,返回一个错误码

陷入阻塞并释放锁

#include <pthread.h>
// wait on a condition 
int pthread_cond_wait(
		pthread_cond_t *cond,		// 条件变量的指针
	pthread_mutex_t *muten		// 要操作(释放和获取)的锁
)// 返回值: 成功返回0;否则,返回一个错误码

唤醒以指定条件变量阻塞的线程, 并使其重新获取锁

#include <pthread.h>
// signal a condition 
int pthread_cond_signal(
		pthread_cond_t *cond		// 条件变量的指针
)// 返回值: 成功返回0;否则,返回一个错误码

销毁条件变量

#include <pthread.h>
// destroy a condition variables
int pthread_cond_destroy(
		pthread_cond_t *cond		// 条件变量的指针
)// 返回值: 成功返回0;否则,返回一个错误码

改进卖票程序

#include <my_header.h>

typedef  struct share_state{
    int flag;   // 1表示加过一次票了,0表示没加过
    int ticketNum;
    pthread_mutex_t mLock;
    pthread_cond_t cond;
}share_state_t;

void *sell(void *arg) {
    share_state_t *shareState = (share_state_t *) arg;
    while(1) {
        pthread_mutex_lock(&shareState->mLock);
        // 判断是否小于等于0张,若小于等于0且已经加过票了,那就释放锁退出循环
        if (shareState->ticketNum <= 0 && shareState->flag == 1) {
            pthread_mutex_unlock(&shareState->mLock);
            break;
        }
        // 没有加过票,那么若小于等于0张,则不卖,释放锁,到下一个循环
        // 大于0张那么就卖票
        struct timeval now;
        gettimeofday(&now,NULL);
        srand(now.tv_usec);
        double rate = rand() * 1.0 / RAND_MAX;
        // printf("rate : %lf\n",rate);
        if (rate <= 0.5 && shareState->ticketNum > 0) {
            shareState->ticketNum--;
            printf("sell a ticket now ticketNum : %d\n",shareState->ticketNum);
        }
        // 小于等于5张,就会指定阻塞,可以发现这里有指定哪个锁
        if (shareState->ticketNum <= 5) {
            pthread_cond_signal(&shareState->cond);
        }
        pthread_mutex_unlock(&shareState->mLock);
        // sleep(1);
    }
    return NULL;
}

void *add(void *arg) {
    share_state_t *shareState = (share_state_t *) arg;
     while(1) {
         pthread_mutex_lock(&shareState->mLock);
         // printf("add,ticketNum:%d\n",shareState->ticketNum);
         // 大于5张就阻塞,小于5张被唤醒
         if (shareState->ticketNum > 5) {
             pthread_cond_wait(&shareState->cond,&shareState->mLock);
         }
         shareState->flag = 1;
         shareState->ticketNum += 10;
         printf("add 10 tickets,now ticketNum: %d\n",shareState->ticketNum);
         pthread_mutex_unlock(&shareState->mLock);
         break;
         // sleep(1);
     }
    return NULL;
}


int main(int argc, char *argv[])
{                    
    share_state_t shareState;
    shareState.flag = 0;
    shareState.ticketNum = 20;
    pthread_mutex_init(&shareState.mLock,NULL);
    // 初始化 cond
    pthread_cond_init(&shareState.cond,NULL);
    pthread_t pid1,pid2;
    pthread_create(&pid1,NULL,sell,&shareState);
    pthread_create(&pid2,NULL,add,&shareState);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    pthread_mutex_destroy(&shareState.mLock);
    return 0;
}

pthread_cond_timedwait

pthread_cond_timedwait是一个可设置超时的pthread_cond_wait

// pthread_cond_timedwait返回有两种可能的情况。如果在设置的等待时间内条件变量被其他线程唤醒并满足条件,函数会返回0。如果等待超时,函数会返回ETIMEDOUT。
函数定义

条件变量的声明/初始化/唤醒/销毁, 皆同pthread_cond_wait

#include <pthread.h>
// wait on a condition 
int pthread_cond_timedwait(
		pthread_cond_t *cond,		// 条件变量的指针
	pthread_mutex_t *muten,		// 要操作(释放和获取)的锁
	timespec *abstime			// 超时时间
)// 返回值: 成功返回0;否则,返回一个错误码
// timespec类型   
struct timespec
{
__time_t tv_sec;//秒
__syscall_slong_t tv_nsec; //纳秒
};

// __time_t  ->  typedef long int __time_t;
// __syscall_slong_t  ->  typedef long int __syscall_slong_t; 
代码示例
#include <testfun.h>

int main(int argc,char*argv[])
{

pthread_mutex_t mLock;
pthread_cond_t cond;
pthread_mutex_init(&mLock, NULL);
pthread_cond_init(&cond, NULL);

pthread_mutex_lock(&mLock);

time_t now = time(NULL);
struct timespec end;
end.tv_sec = now+10;
end.tv_nsec = 0;

int res = pthread_cond_timedwait(&cond, &mLock, &end);
THREAD_ERROR_CHECK(res, "timedwait");

pthread_mutex_unlock(&mLock);

return 0;
}

pthread_cond_broadcast

函数说明

pthread_cond_broadcast 用于唤醒以指定条件变量阻塞的线程, 并使其重新获取锁, 但是和pthread_cond_signal不同的是, 虽然都是唤醒阻塞线程, pthread_cond_signal 每次从阻塞队列中取出一个唤醒, 而pthread_cond_broadcast 是以广播的方式把指定条件变量的阻塞队列线程全部唤醒.

ps: 要注意虚假唤醒问题(eg: 以一个生产者唤醒多个消费者为例)

// 虚假唤醒: pthread_cond_broadcast 唤醒了多个线程, 每一个进程一定会立即获得锁? 不会
eg: 广播: 唤醒了5个线程, 5个线程使用的还是同一把锁, 这个醒的5个线程, 都要去试图获取那个锁, 1个成功, 4个等待锁
1个成功获得锁的线程做了针对性的事情, 导致条件变化了; 
--> 其余线程喊醒了, 不满足条件, 做不了事, -> 虚假唤醒
--> 一种良好的解决办法
while(num >= 5){
    重新wait;
}


-->  pthread_cond_broadcast 唤醒了多个线程: 要获取的不是同一把锁, 分别去试图获取自己设置的锁 
代码示例
#include <testfun.h>

typedef struct share_value{
  int num;
  pthread_mutex_t mLock;
  pthread_cond_t cond;
} share_value_t;

void *fun(void *arg){
  share_value_t *pShareValue = (share_value_t *)arg;

  pthread_mutex_lock(&pShareValue->mLock);
  pShareValue->num++;
  int childNum = pShareValue->num;
  pthread_mutex_unlock(&pShareValue->mLock);

  sleep(childNum);
  printf("i am %d child thread \n", childNum);


  pthread_mutex_lock(&pShareValue->mLock);
  printf("i am %d child thread before \n", childNum);
  pthread_cond_wait(&pShareValue->cond,&pShareValue->mLock);
  printf("i am %d child thread after \n", childNum);
  pthread_mutex_unlock(&pShareValue->mLock);

}
int main(int argc,char*argv[])
{
  share_value_t shareValue;
  shareValue.num = 0;
  pthread_mutex_init(&shareValue.mLock, NULL);
  pthread_cond_init(&shareValue.cond, NULL);

  pthread_t pid1, pid2;
  pthread_create(&pid1, NULL, fun, &shareValue);
  pthread_create(&pid2, NULL, fun, &shareValue);

  sleep(5);
  pthread_mutex_lock(&shareValue.mLock);
  pthread_cond_broadcast(&shareValue.cond);
  // pthread_cond_signal(&shareValue.cond);
  pthread_mutex_unlock(&shareValue.mLock);

  pthread_join(pid1, NULL);
  pthread_join(pid2, NULL);

  pthread_mutex_destroy(&shareValue.mLock);
  pthread_cond_destroy(&shareValue.cond);

  return 0;
}

简单练习

1

现在有两个线程t1和t2,t1 打印 A 和 C,t2 打印 B。书写代码,使用条件变量每次的显示顺序都是A->B->C。

#include <my_header.h>


pthread_mutex_t key1;
pthread_mutex_t key2;

void *t1(void *arg) {
    printf("A->");
    pthread_mutex_unlock(&key2);
    pthread_mutex_lock(&key1);
    printf("C\n");
    pthread_mutex_unlock(&key1);
    return NULL;
}

void *t2(void *arg) {
    pthread_mutex_lock(&key2);
    printf("B->");
    pthread_mutex_unlock(&key1);
    return NULL;
}




int main(int argc, char *argv[])
{   
    pthread_mutex_init(&key1,NULL);
    pthread_mutex_init(&key2,NULL);
    pthread_t pid1,pid2;
    pthread_mutex_lock(&key1);
    pthread_mutex_lock(&key2);
    pthread_create(&pid1,NULL,t1,NULL);
    pthread_create(&pid2,NULL,t2,NULL);
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    return 0;
}

image-20240509231216218

  • 37
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值