线程-线程同步

来源:互联网 发布:js 方法return 节点 编辑:程序博客网 时间:2024/06/16 04:41

我们知道线程共享同一进程内的资源。如果每个线程使用的变量,其他线程都不会读取个修改,那么就不存在一致性问题。相同的,如果变量只读,多个线程同时读取该变量也不会存在一致性问题。但是,当一个线程可以修改变量,其他线程可以读取或修改变量,那么就要对这些线程进行同步,确保多个线程访问数据安全,不会访问到无效数据。
两个或多个线程同时修改同一变量时,也需要同步。跟前面信号讲的问题相同,考虑增量操作情况。增量操作通常可以分为三个步骤:
(1)从内存单元中读入寄存器
(2)从寄存器变量进行增量操作
(3)将寄存器的值写回内存
这就和信号量当时的可重入性是同样的问题。
对于多线程程序,访问冲突的问题是很普遍的,解决的办法就是引入互斥锁。

互斥量

互斥量本质来说是一把锁,在访问共享资源前对互斥量进行设置(加锁),在访问完成后释放 (解锁)互斥量。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直到当前线程释放该互斥锁。
互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量之前,比如首先对它进行初始化,可以把它设置为常量PTHREAD_MUTEX_INITIALIZER(静态分配),也可以调用pthread_mutex_init函数进行初始化。动态分配互斥量,释放前需要调用pthread_mutex_destory。

 #include <pthread.h>       int pthread_mutex_destroy(pthread_mutex_t *mutex);       int pthread_mutex_init(pthread_mutex_t *restrict mutex,              const pthread_mutexattr_t *restrict attr);       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;       两函数返回值:若成功,返回0;否则返回错误编码

attr设置为NULL,用默认初始化互斥量。

加锁解锁函数:
对互斥量进行加锁,调用pthread_mutex_lock。如果互斥量已经上锁,调用线程将会阻塞,知道互斥量被解锁。对互斥量解锁,嗲用pthread_mutex_unlock。

include <pthread.h>       int pthread_mutex_lock(pthread_mutex_t *mutex);//       int pthread_mutex_trylock(pthread_mutex_t *mutex);       int pthread_mutex_unlock(pthread_mutex_t *mutex);     返回值:若成功,返回;否则,返回错误编码

当然如果线程不希望被阻塞,可以调用trylock对互斥量加锁。

/*************************************************************************    > File Name: mutex.c    > Author:     > Mail:     > Created Time: Thu 15 Jun 2017 11:35:04 PM PDT ************************************************************************/#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>int count=0;void *thread(void *tval){    int i=0;      while(i<5001)    {        int tmp=count;        i++;        printf("pthread:%lu,count:%d\n",pthread_self(),count);        count=tmp+1;    }      return NULL;}int main(){    pthread_t tid1;    pthread_t tid2;    pthread_create(&tid1,NULL,&thread,NULL);    pthread_create(&tid2,NULL,&thread,NULL);    pthread_join(tid1,NULL);    pthread_join(tid2,NULL);    printf("count:%d",count);    return 0;}

这里写图片描述
很明显这不对,两个线程tid1和tid2在跑while中的逻辑的时候,这个时候就会发生访问冲突,count本来应该是10001的,但是因为访问冲突,所以最终的结果count结果要小于10000,原因上面我已经介绍过了,就不再重复,要想实现互斥,我们加上互斥锁就好了。
使用互斥量之后:

/*************************************************************************    > File Name: mutex.c    > Author:     > Mail:     > Created Time: Thu 15 Jun 2017 11:35:04 PM PDT ************************************************************************/#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>int count=0;static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;void *thread(void *tval){    int i=0;      while(i<5001)    {        pthread_mutex_lock(&mutex);        int tmp=count;        i++;        printf("pthread:%lu,count:%d\n",pthread_self(),count);        count=tmp+1;        pthread_mutex_unlock(&mutex);    }      return NULL;}int main(){    pthread_t tid1;    pthread_t tid2;    pthread_create(&tid1,NULL,&thread,NULL);    pthread_create(&tid2,NULL,&thread,NULL)    pthread_join(tid2,NULL);    pthread_join(tid1,NULL);     pthread_join(tid2,NULL);    printf("coun`:%d",count);    return 0;}

这里写图片描述
加入互斥锁之后的值才是对的。

死锁

什么是死锁?
所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
什么时候可能产生死锁?
(1)当一个线程对互斥量加锁两次。
(2)两个线程都互相加锁,并且不释放。因为两个都在互相请求另一个线程资源,所以两线程都无法向下运行,也是产生死锁。
产生死锁的原因主要是:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
怎么避免死锁呢?
(1)加锁顺序(线程按照一定的顺序加锁)
(2)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
(3)死锁检测(每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph)等等将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。
当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生)

条件变量

条件变量是线程可用的另一种同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
条件本身是由互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量。其他线程获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件。
条件变量相关函数和互斥量相关函数大同小异。

  • 初始化和释放条件变量
 #include <pthread.h>       int pthread_cond_destroy(pthread_cond_t *cond);       int pthread_cond_init(pthread_cond_t *restrict cond,              const pthread_condattr_t *restrict attr);       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;       返回值:若成功,返回0;否则,返回错误编号

和互斥量一样,可以直接用宏来初始化。attr可以为NULL。

  • 等待条件变量为真
#include <pthread.h>       int pthread_cond_timedwait(pthread_cond_t *restrict cond,              pthread_mutex_t *restrict mutex,              const struct timespec *restrict abstime);       int pthread_cond_wait(pthread_cond_t *restrict cond,              pthread_mutex_t *restrict mutex);     返回值:若成功,返回0;否则,返回错误编码

传递给pthread_cond_wait的互斥量对条件进行保护。调用者把锁住的互斥量传递给函数,函数然后自动把调用线程放到等待条件的线程列表上,并对互斥量解锁。
pthread_cond_timedwait函数功能与pthread_cond_wait函数相似,只是多了一个超时。

  • 通知线程条件满足
 #include <pthread.h>       int pthread_cond_broadcast(pthread_cond_t *cond);       int pthread_cond_signal(pthread_cond_t *cond);       返回值:若成功,返回0;否则,返回错误编码

下篇通过一个基于消费者生产者的模型来做例子。

原创粉丝点击