互斥锁指代相互排斥,它是最基本的同步形式。互斥锁用于保护临界区,以保证任何时刻只有一个线程在执行其中的代码。
如下
lock_THE_MUtex(...)
临界区
unlock_the_mutex(...)
互斥锁的上锁和解锁一般通过下列三个函数
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_trylock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr);
以上三个函数返回值均相同,成功返回0,出错返回错误值;
互斥锁的简单应用
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥锁的声明
void* thread_fun1(void *arg)
{
printf("This is thread fun 1.\n");
pthread_mutex_lock(&mutex); //线程1上锁
printf("thread fun 1 lock.\n")
sleep(3);
printf("thread fun 1 unlock.\n");//线程1解锁
pthread_mutex_unlock(&mutex);
}
void* thread_fun2(void *arg)
{
printf("This is thread fun 2.\n");
pthread_mutex_lock(&mutex);//线程2上锁
printf("thread fun 2 lock.\n");
pthread_mutex_unlock(&mutex);//线程2解锁
printf("thread fun 2 unlock.\n");
}
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread_fun1, NULL);
sleep(1); //保证线程2能在线程1之后被创建
pthread_create(&tid2, NULL, thread_fun2, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/2b9f89d140dccafc20fe1beaf3a062dc.png)
可以看到创建线程1之后隔了一秒才进行创建线程2,所以先打印出线程1的信息
但由于线程1上锁后停留3秒,减去创建线程2停留的1秒,剩下两秒钟是足够让线程2运行一万次的,但是通过结果我们可以看到
线程2并没有继续运行,这是因为线程1此时已经上了锁,而线程2再次想要进行上锁是不可能了,这个时候就必须等线程1解锁之后才能够上锁。
比较官方的说法是:如果尝试给一个已由另外某个线程锁住的互斥锁上锁,那么pthread_mutex_lock将阻塞到该互斥锁解锁为止。而pthread_mutex_trylock是对应的非阻塞函数,如果该互斥锁已锁住,它就返回一个EBUSY错误。
即在线程1已经上锁的情况下线程2使用
pthread_mutex_trylock仍旧可以进行获取锁,以达到让线程继续执行的目的。pthread_mutex_trylock(pthread_mutex_t *nptr)的返回值上面已经说过就不再赘述。
条件变量是类型为pthread_cond_t的变量
与之相关的函数有
#include<pthread.h>
int pthread_cond_wait(pthread_cond_t *cptr,pthread_mutex_t *mptr);
int pthread_cond_signal(pthread_cond_t *cptr);
返回值类似互斥锁,成功返回0,出错返回正的错误值。
简单用法
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
void* thread_fun1(void *arg)
{
int index = *(int*)arg;
printf(" %d thread start\n",index);
pthread_mutex_lock(&mutex);
printf(" %d thread cond wait\n",index);
pthread_cond_wait(&cond,&mutex);
printf(" %d thread waked up\n",index);
pthread_mutex_unlock(&mutex);
}
void* thread_fun2(void *arg)
{
printf("this is thread 2\n");
pthread_mutex_lock(&mutex);
printf("cond signal thread\n");
sleep(2);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t tid1[5],tid2;
int i;
for(i=0;i<5;++i)
{
pthread_create(&tid1[i],NULL,thread_fun1,&i);
sleep(1);
}
sleep(2);
pthread_create(&tid2,NULL,thread_fun2,NULL);
for(i=0;i<5;++i)
{
pthread_join(tid1[i],NULL);
}
pthread_join(tid2,NULL);
return 0;
}
运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/dc5358708233ca758b066d3c022a4ee3.png)
例子很简单,循环创建5个线程并进行上锁解锁,在临界区用pthread_cond_wait函数使线程进入阻塞状态。
pthread_cond_signal为其唤醒函数,但一次只能唤醒一个线程,所以这里采用pthread_cond_broadcast函数将其全部唤醒,线程被唤醒后,在阻塞的地方继续运行。关于pthread_cond_broadcast函数的用法就不再赘述了,用法跟signal是一样的。这些函数其实用法都大同小异。还有pthread_cond_timewait等函数用法类似。
值得注意的是pthread_cond_timewait允许线程就阻塞时间设置一个限制值。而时间参数是一个timespec结构
struct timespec
{
time_t tvsec; /* seconds */
long tv_nsec; /* nanoseconds */
}
雨下整夜,我的爱溢出就像雨水
嘻嘻嘻