线程同步的方法—互斥锁、信号量和条件变量

来源:互联网 发布:厦门安胜网络怎么样 编辑:程序博客网 时间:2024/05/16 19:39

在说线程的同步异步之前,先说一下进程的同步与异步,因为线程的同步与异步基本上是一个概念,只是将进程之间的关系修改为线程之间的关系 。

1、同步:当一个进程/线程在执行某个请求的时候,请求的信息需要等一段时间才能够返回,那么该进程/线程就一直等待,直到请求的信息返回。

2、异步:当一个进程/线程在执行某个请求的时候,不必等待请求信息的返回,直接执行接下来的操作。不管其他进程/线程的状态。当有消息返回时系统会通知进程/线程进行处理,这样可以提高执行的效率。

简单来说:同步需要等待,异步不需要等待。

全局变量共享-》进程内所有的线程都可以操作全局变量。
例如:需要在函数线程中统计用户输入的单词个数,在主线程中获取用户输入,放入全局字符数组。

(1)互斥锁
允许程序员锁住某个对象,使得每次只能有一个线程访问他。为了控制关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后再完成操作之后解锁它。
用于互斥锁的基本函数:

#include<stdio.h>int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);//初始化int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁锁

用互斥锁解决上面问题:

代码实现:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <assert.h>#include <pthread.h>pthread_mutex_t mutex;char buff[128]={0};void *fun(void *arg){    while(1)    {        pthread_mutex_lock(&mutex);        int i=0;        int count=0;        for(;i<strlen(buff);++i)        {            if(isalpha(buff[i]) && !isalpha(buff[i+1]))            {                count++;            }        }        printf("count == %d\n",count);        pthread_mutex_unlock(&mutex);        sleep(1);    }}void main(){    pthread_mutex_init(&mutex,NULL);    pthread_t id;    int res=pthread_create(&id,NULL,fun,NULL);    assert(res==0);    while(1)    {        pthread_mutex_lock(&mutex);        printf("Input: \n");        fflush(stdout);        fgets(buff,128,stdin);        buff[strlen(buff)-1]=0;        if(strncmp(buff,"end",3)==0)        {            break;        }        pthread_mutex_unlock(&mutex);        sleep(1);    }}

(2)信号量
信号量的基本函数:

#include<semaphore.h>int sem_init(sem_t *sem,int pshared,unisigned int value);//初始化int sem_wait(sem_t *sem);//相当于进程中的P操作int sem_post(sem_t *sem);//相当于进程中的V操作int sem_destroy(sem_t *sem);//用完信号良好对它进行清理

针对上面用互斥锁解决的问题,用信号量解决,代码如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <assert.h>#include <pthread.h>#include <semaphore.h>sem_t sem;char buff[128]={0};void *fun(void *arg){    while(1)    {                sem_wait(&sem);        int i=0;        int count=0;        for(;i<strlen(buff);++i)        {            if(isalpha(buff[i]) && !isalpha(buff[i+1]))            {                count++;            }        }        printf("count == %d\n",count);    }}void main(){    sem_init(&sem,0,0);    pthread_t id;    int res=pthread_create(&id,NULL,fun,NULL);    assert(res==0);    while(1)    {            printf("Input: \n");        fflush(stdout);        fgets(buff,128,stdin);        buff[strlen(buff)-1]=0;        if(strncmp(buff,"end",3)==0)        {            break;        }        sem_post(&sem);    }}

(3)条件变量

在系统死锁中有这么一个典型的实例:
在一条生产线上有一个仓库,当生产者生产的时候需要锁住仓库独占,而消费者取产品的时候也要锁住仓库独占。如果生产者发现仓库满了,那么他就不能生产了,变成了阻塞状态。但是此时由于生产者独占仓库,消费者又无法进入仓库去消耗产品,这样就造成了一个僵死状态。

我们需要一种机制,当互斥量被锁住以后发现当前线程还是无法完成自己的操作,那么它应该释放互斥量,让其他线程工作。
A、可以采用轮询的方式,不停的查询你需要的条件
B、让系统来帮你查询条件,使用条件变量pthread_cond_t cond

条件变量使用之前需要初始化

a、pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

b、int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);//默认属性为空NULL

条件变量使用完成之后需要销毁
int pthread_cond_destroy(pthread_cond_t *cond);

条件变量使用需要配合互斥量

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

a、使用pthread_cond_wait等待条件变为真。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传递给函数。
b、这个函数将线程放到等待条件的线程列表上,然后对互斥量进行解锁,这是个原子操作。当条件满足时这个函数返回,返回以后继续对互斥量加锁。

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

c、这个函数与pthread_cond_wait类似,只是多一个timeout,如果到了指定的时间条件还不满足,那么就返回。时间用下面的结构体表示
struct timespec{
time_t tv_sec;
long tv_nsec;
};
注意,这个时间是绝对时间。例如你要等待3分钟,就要把当前时间加上3分钟然后转换到 timespec,而不是直接将3分钟转换到 timespec

当条件满足的时候,需要唤醒等待条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
a、pthread_cond_broadcast唤醒等待条件的所有线程
b、pthread_cond_signal至少唤醒等待条件的某一个线程
注意,一定要在条件改变以后在唤醒线程
简单来说:同步需要等待,异步不需要等待。

注:后面关于条件变量的总结是转载别人的,因为觉得有用,而且总结得很好。

阅读全文
0 0
原创粉丝点击