linux下多进程和多线程编程之二(同步)
来源:互联网 发布:windows live账号注册 编辑:程序博客网 时间:2024/04/30 11:08
多进程,多线程的同步
多进程和多线程的同步,我们重点讲解下多线程下的同步,这个是编程中最常用的。
多线程同步,总结以下几种机制方式
第一:互斥锁
互斥锁的原理是利用互斥性,保证程序在某一刻只有一个线程执行一段关键部分的代码。
互斥变量的创建:有两种方式,第一个是pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER。利用赋值的方式做静态赋值
第二个是利用函数int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);进行初始化一个互斥变量
然后使用函数int pthread_mutex_lock(pthread_mutex *mutex);顾名思义,这个函数就是对其进行上锁。然后开始执行关键部分的代码。
最后需要解锁使用int pthread_mutex_unlock(pthread_mutex *mutex); 这个函数就是对其进行解锁,然后其它线程才能获取到锁,进行上锁
等这把锁使用完毕,程序退出时,我们需要进行将锁释放,使用函数int pthread_mutex_destroy(pthread_mutex *mutex);
如下代码所示
#include<pthread.h>#include<stdio.h>#include<unistd.h>void* fun(void* i);pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//用赋值的方法初始化一个互斥量,一把锁int count;//定义一个全局变量,让所有的线程可以共享这个全局变量int main(int argc,char* argv[]){ int i=0; pthread_t t[10]; for(i=0;i<10;i++) { pthread_create(&t[i],NULL,&fun,NULL); } for(i=0;i<10;i++) { pthread_join(t[i],NULL); }}void* fun(void* i){ if(pthread_mutex_lock(&mutex))//执行成功返回0 { //失败的时候,需要做相应的处理 } printf("count=%d\n",count++);//因为需要对这个全局变量进行操作,所以我们需要进行加锁,这部分可以称为是关键代码,也是线程需要同步的代码部分 if(!pthread_mutex_unlock(&mutex))//执行成功返回0 { //如果解锁失败了.那么我们需要做相应的处理,因为如果解锁失败那么其它几个线程可能会出现死锁现象,程序阻塞 }}
以上程序运行结果是:
count=0
count=1
count=2
count=3
count=4
count=5
count=6
count=7
count=8
count=9
利用互斥量保证了线程的并发和同步,这里需要对线程加解锁函数调用做一次判断,以免程序出现死锁问题.
第二:条件变量
条件变量的产生是为了当多线程下,某个条件产生的时候,去执行一段代码。通常条件变量和互斥锁同时使用。
条件变量的创建和互斥量一样,都有两种方式,一种静态赋值,一种动态使用函数的方式。静态赋值的方式:pthread_cond_t cond=PTHREAD_COND_INITIALIZER,动态调用函数的方式:intpthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略.
条件变量的等待:有两种方式等待:第一是条件等待:int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex),第二种是计时等待:int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime),计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。但是无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait())的竞争条件(Race Condition)。
条件变量的激发:激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
条件变量的注销:注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。
条件变量的使用最经典的模式是生产者与消费者模式,下面就用代码描述这个模式。
#include<pthread.h>#include<stdio.h>#include<unistd.h>void* fun(void* i);void* fun1(void* i);pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;pthread_cond_t ccon=PTHREAD_COND_INITIALIZER;pthread_cond_t pcon=PTHREAD_COND_INITIALIZER;int size=0;char store[5];void print()//打印{ int i=0; for(i=0;i<size;i++) printf("%c ",store[i]); printf("\n");}int main(int argc,char* argv[]){ int i=0; pthread_t t[10]; pthread_create(&t[0],NULL,&fun,NULL);//消费者 pthread_create(&t[1],NULL,&fun1,NULL);//生产者 pthread_create(&t[2],NULL,&fun1,NULL);//生产者 pthread_join(t[0],NULL); pthread_join(t[1],NULL); pthread_join(t[2],NULL);}void* fun(void* i)//消费者{ int count=0; while(count<52) { if(pthread_mutex_lock(&mutex))//执行成功返回0 { //失败的时候,需要做相应的处理 } if(size==0)//需要生产了,消费品已经消费完 { pthread_cond_wait(&ccon,&mutex); } printf("pop date:%c\n",store[size-1]);//消费 count++; size--;//消费了,所以减去1 print(); pthread_cond_signal(&pcon);//通知生产着可以生产了 if(pthread_mutex_unlock(&mutex))//执行成功返回0 { //如果解锁失败了.那么我们需要做相应的处理,因为如果解锁失败那么其它几个线程可能会出现死锁现象,程序阻塞 } }}void* fun1(void* i){ char count='A'; while(count<='Z') { if(pthread_mutex_lock(&mutex))//执行成功返回0 { //失败的时候,需要做相应的处理 } if(size==5) { pthread_cond_wait(&pcon,&mutex);//等待消费者去消费 } store[size++]=count;//生产了一个数据 printf("push data:%c\n",count); count++; print(); pthread_cond_signal(&ccon);//通知消费者消费,这个时候肯定是有可以消费的 pthread_mutex_unlock(&mutex); }}
其中有两个生产者,一个消费者,这里需要注意的是,生产者和消费者尽量需要总生产数量和总消费数量一样。因为可能会出现,当消费者消费到M个退出 不在消费,也就不会再发激发信号,那么生产者继续生产,一直生产满了的时候,就会等消费者激发,但是这个时候消费者已经结束了,不再激发条件变量,导致了生产者进入了阻塞。如果不确定生产者和消费者的数量时候,我们可以用用pthread_cond_timedwait()函数去执行等待,设个时间,超过时间限制,就不再等待。
总结条件变量:条件变量的运用,一般运用在多线程编程下面有一些代码需要满足某个条件成立的情况下才会执行的情况,条件变量也需要和互斥锁同时用。
第三:信号量
信号量不仅常作用于线程的同步,也作用于进程的同步,信号量的产生是为了满足一些资源在同一个时刻可以让N个线程或者进程同时访问,但是只能小于N个的进程或者线程同时访问,大于N的时候,就会被阻塞。信号量可以说是加强版的互斥锁,因为当信号量为1的时候,就是互斥锁的功能,也就某一个时刻只能一个线程使用。
信号箱的函数使用一共有两套IPI函数,根据ipc的结构不同,一套是system ipc。一套是POSIX IPC。
POSIX IPC 的每个ipc都是有名称的,mq_open sem_open shm_open三个函数的第一个参数就是这个名称,这个名称不一定是在文件系统中存在的名称。要使用IPC对象,需要创建或者打开,这与文件操作类似,并且与文件类似,也有相对应的操作权限的。
System v ipc中有一个重要的类型是key_t,在msget、semget、shmget函数操作中都需要利用这个类型是参数。系统中对每个ipc对象都会有一个结构体来标识。
注:mq_open shm_open和msget、shmget等函数将在多进程和多线程之通信中介绍。可参考那篇文章
一种是简单信号量(Posix ),另一种是用于进程间通讯的信号量集(System);
属于POSIX标准的信号量;
从信号量的命名来看,信号量又可分为命名信号量和匿名(未命名)信号量;
从信号量的值来看,信号量可分为二进制信号量和计数信号量;
命名信号量与匿名信号量不同,它一般只用于在进程之间进行资源同步,而且是使用文件全路径来对信号量进行命名的;命名信号量具有属主用户ID、组ID和保护模式等参数;命名信号量的名称是在文件系统的命名空间中定义的;
匿名信号量的操作函数有:int sem_init(sem_t *sem, int pshared, unsigned int value);创建一个信号量,并根据value值来得到信号量的数量,pshared取0和非0,当其为0的时候,就是用于线程间的同步作用,当前为非0的时候,就是用于进程间的通信,但是由于现在linux目前没有实现进程间共享信号量,所以这个值只能是0。
#include<stdio.h>#include<pthread.h>#include<semaphore.h>#include<time.h>sem_t sem;//一个信号量void* run(void* par){ sem_wait(&sem); printf("第%d个线程开始申请连接资源\n",(int)par+1); sleep(5); int x=0; sem_getvalue(&sem,&x); printf("第%d个线程开始释放资源\n",(int)par+1); sem_post(&sem);}int main(int argc,char* argv[]){ sem_init(&sem,0,10); pthread_t t[15]; int i=0; for(i=0;i<15;i++) { pthread_create(&t[i],NULL,&run,(void*)i); } for(i=0;i<15;i++) { pthread_join(t[i],NULL); } sem_destroy(&sem);}
以上代码详细的描述了,信号量在多线程的时候对资源的申请,这里信号量有10个大小,保证资源在同一个时刻可以让十个线程对其操作.
信号量集的初始化操作由shmget函数完成;这点我们在多进程多线程通信上讲解,这里不做多的描述.读者可查看多进程多线程编程之三通信。
- linux下多进程和多线程编程之二(同步)
- linux下多进程和多线程编程之一(使用)
- linux多进程和多线程分析之二
- linux多进程和多线程分析之二 .
- Linux多线程编程(二)线程同步之条件变量
- 转【linux下多进程、多线程编程】
- linux下多进程、多线程编程
- linux下多进程、多线程编程
- Linux下多进程/多线程编程
- linux下多进程/多线程编程总结
- linux下多进程、多线程编程
- linux下多进程、多线程编程
- linux 多线程 多进程同步
- linux下的多线程/多进程同步/通信机制
- 浅谈linux下多进程编程及其同步机制
- Linux 多线程/进程同步
- linux/unix多线程/多进程编程总结(二)
- linux多线程编程---线程同步之互斥锁
- JDK,JRE,JVM区别与联系
- disruptor使用示例
- Cocos2d-x数据模块
- Git入门教程(适合新手)
- SVN 本地文件路径修改后 会提示missing xxx 文件
- linux下多进程和多线程编程之二(同步)
- Mac 下的终端启动自动补全,忽略大小写
- JS-Cookie
- 转换Unicode (JS)
- 模拟线程Timer(JS)
- 取消的板單新增功能
- Unity3D之触摸输入单击与双击研究
- HTML通过button触发input-file控件上传文件的问题
- 多附件上传