一、锁(互斥锁)
什么是互斥锁呢,我给大家举个例子,我们可以想象成是车站的座位,然后把线程比喻成一个人,你进车站候车的时候,你就要先找到没有人的座位,然后坐下去(表示你正在使用),这样其他的人(其他的线程)就无法再使用这个座位了,如果想要使用该座位就需要你站起来,别人才可以使用。
并发:同时发生
在生活中,可能会有多个执行单元,同时访问同一个资源。比如座位。使用锁(互斥锁)来保护资源
互斥锁:一个时刻值允许一个执行单元访问共享资源
1.定义并初始化一把锁
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
2.访问共享资源前
pthread_mutex_lock(pthread_mutex_t *mutex);
该函数,如果发现锁 是开的,直接关闭该锁,然后继续执行....
如果发现 锁是 关的, 该函数 会让线程 阻塞/睡眠,等待别人解锁唤醒...............................
直到对方开锁.... ...获取锁,关锁..访问...
3.访问完毕解锁
pthread_mutex_unlock(pthread_mutex_t *mutex);
可能会去唤醒等待的线程........
实现代码:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
struct msg_struct {
int aa;
int bb;
char des[32];
};
pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
struct msg_struct data;
void *thread_write(void *argc)
{
int m=1;
while(1){
pthread_mutex_lock(&mylock);
m++;
data.aa=m*11;usleep(1000*1000);
data.bb=m*111; usleep(1000*1000);
//if(发现错误){ pthread_exit(); } 结果是没有解锁,然后 对方会被卡死.
strcpy(data.des,"hello,I am writer");
pthread_mutex_unlock(&mylock);
usleep(1000*300);
}
return NULL;
}
void *thread_read(void *argc)
{
while(1){
pthread_mutex_lock(&mylock);
printf("read aa=%d bb=%d\n",data.aa,data.bb);
pthread_mutex_unlock(&mylock);
usleep(300);
}
return NULL;
}
int main(int argc,char **argv)
{
pthread_t rtid,wtid;
pthread_create(&wtid,NULL,thread_write,NULL );
pthread_detach(wtid);
pthread_create(&rtid,NULL,thread_read,NULL );
pthread_detach(rtid);
while(1){
printf("I am main thread\n");
sleep(5);
}
return 0;
}
二、死锁
1. 某个线程加锁之后忘了解锁,另一个线程 被卡死。
2. 如果需要同时访问多个共享资源,即需要同时获取多把锁。
见图,解决之道:要求所有执行单元必须顺序访问
三、 线程的条件变量: 线程通知
有时候线程需要等待 某个事件/数据的到来,然后处理,处理完毕后继续等待。线程分为提交者,和消费者
1.消费者线程
while(1){
在##指定位置##睡眠等待数据(等待被唤醒) xx_wait()
处理数据
}
2.提交者线程
while(1){
提交数据
发送通知,去##指定位置##唤醒 xx_signal();
}
实现代码:
#include <stdio.h>
#include <pthread.h>
struct msg_struct {
int val1;
int val2;
int result;
char des[32];
};
int data_flags =0 ; //0-无数据 1-有数据
struct msg_struct data;
pthread_mutex_t mutex_cond; //保护条件变量的锁, 因为两个线程可能同时访问它,造成并发问题
pthread_cond_t wait_cond; //等待者线程在这里睡眠, 提交者线程去这里唤醒睡眠的线程
// 床
void *wait_thread_fun(void *args)
{
while(1){
if(data_flags == 0){
/*
睡眠等待被唤醒,线程卡死这里
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
cond,线程在 cond这里睡眠,等待被唤醒.
*/
pthread_mutex_lock(&mutex_cond);
pthread_cond_wait(&wait_cond,&mutex_cond );
/*
wait线程持有了锁,然后 去睡眠去了...... 问题来了,submit线程这时候就会被卡住.....
pthread_cond_wait()
{
...要去睡眠了,睡眠之前...释放锁...
睡眠中.....,等待被唤醒..
----被唤醒..., 再次上锁..
}
*/
/*线程已经被唤醒了....*/
pthread_mutex_unlock(&mutex_cond);
}
data.result = data.val1+data.val2;
printf("wait thread got val1=%d val2=%d,result=%d\n",\
data.val1,data.val2,data.result);
data_flags = 0; //表示我已经处理完毕了数据.....
}
return NULL;
}
void *submit_thread_fun(void *args)
{
int m=234;
while(1){
sleep(3);
m+=10;
data.val1 = m*10;
data.val2 = m+111;
data_flags = 1;
/*
去指定位置 唤醒所有等待的 线程
int pthread_cond_signal(pthread_cond_t *cond);
cond: 条件变量, 等待者线程 在这里睡眠, 提交者线程去这里唤醒睡眠的线程.
返回值: 0-success, 失败 错误码
*/
pthread_mutex_lock(&mutex_cond);
int ret = pthread_cond_signal(&wait_cond);
pthread_mutex_unlock(&mutex_cond);
if(ret){
printf("signal err %s\n",strerror(ret) );
return NULL;
}
}
return NULL;
}
int main(void)
{
pthread_t wait_tid;
pthread_t submit_tid;
pthread_create(&wait_tid, NULL,wait_thread_fun,NULL);
pthread_create(&submit_tid, NULL,submit_thread_fun,NULL);
pthread_detach(wait_tid );
pthread_detach(submit_tid);
while(1){
printf("I am main \n");
sleep(5);
}
return 0;
}