范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

多线程编程(c多线程编程)

  多线程编程(c++多线程编程)
  多线程编程可以说每个程序员的基本功,同时也是开发中的难点之一,本文以Linux C为例,讲述了线程的创建及常用的几种线程同步的方式,最后对多线程编程进行了总结与思考并给出代码示例。一、创建线程
  多线程编程的第一步,创建线程。创建线程其实是增加了一个控制流程,使得同一进程中存在多个控制流程并发或者并行执行。
  线程创建函数,其他函数这里不再列出,可以参考pthread.h。#include<pthread.h>intpthread_create(pthread_t*restrictthread,/*线程id*/  	constpthread_attr_t*restrictattr,/*线程属性,默认可置为NULL,表示线程属性取缺省值*/  	void*(*start_routine)(void*),/*线程入口函数*/  	void*restrictarg/*线程入口函数的参数*/  	);
  代码示例:#include<stdio.h>#include<string.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>char*thread_func1(void*arg){pid_tpid=getpid();pthread_ttid=pthread_self();printf("%spid:%u,tid:%u(0x%x)\n",(char*)arg,(unsignedint)pid,(unsignedint)tid,(unsignedint)tid);char*msg="thread_func1";returnmsg;  }void*thread_func2(void*arg){pid_tpid=getpid();pthread_ttid=pthread_self();printf("%spid:%u,tid:%u(0x%x)\n",(char*)arg,(unsignedint)pid,(unsignedint)tid,(unsignedint)tid);char*msg="thread_func2";while(1){printf("%srunning\n",msg);  sleep(1);  }returnNULL;  }intmain(){pthread_ttid1,tid2;if(pthread_create(&tid1,NULL,(void*)thread_func1,"newthread:")!=0){printf("pthread_createerror.");exit(EXIT_FAILURE);  }if(pthread_create(&tid2,NULL,(void*)thread_func2,"newthread:")!=0){printf("pthread_createerror.");exit(EXIT_FAILURE);  }  pthread_detach(tid2);char*rev=NULL;  pthread_join(tid1,(void*)&rev);printf("%sreturn.\n",rev);  pthread_cancel(tid2);printf("mainthreadend.\n");return0;  }二、线程同步
  有时候我们需要多个线程相互协作来执行,这时需要线程间同步。线程间同步的常用方法有:
  互斥
  信号量
  条件变量
  我们先看一个未进行线程同步的示例:#include<stdio.h>#include<string.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#defineLEN100000intnum=0;void*thread_func(void*arg){for(inti=0;i<LEN;++i){  num+=1;  }  returnNULL;  }intmain(){pthread_ttid1,tid2;  pthread_create(&tid1,NULL,(void*)thread_func,NULL);  pthread_create(&tid2,NULL,(void*)thread_func,NULL);char*rev=NULL;  pthread_join(tid1,(void*)&rev);  pthread_join(tid2,(void*)&rev);printf("correctresult=%d,wrongresult=%d.\n",2*LEN,num);return0;  }
  运行结果:correct result=200000, wrong result=106860.。
  分享更多关于 Linux后端开发网络底层原理知识学习提升 点击正在跳转获取,完善技术栈,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。
  【1】互斥
  这个是最容易理解的,在访问临界资源时,通过互斥,限制同一时刻最多只能有一个线程可以获取临界资源。
  其实互斥的逻辑就是:如果访问临界资源发现没有其他线程上锁,就上锁,获取临界资源,期间如果其他线程执行到互斥锁发现已锁住,则线程挂起等待解锁,当前线程访问完临界资源后,解锁并唤醒其他被该互斥锁挂起的线程,等待再次被调度执行。
  "挂起等待"和"唤醒等待线程"的操作如何实现?每个Mutex有一个等待队列,一个线程要在Mutex上挂起等待,首先在把自己加入等待队列中,然后置线程状态为睡眠,然后调用调度器函数切换到别的线程。一个线程要唤醒等待队列中的其它线程,只需从等待队列中取出一项,把它的状态从睡眠改为就绪,加入就绪队列,那么下次调度器函数执行时就有可能切换到被唤醒的线程。
  主要函数如下:#include<pthread.h>intpthread_mutex_init(pthread_mutex_t*restrictmutex,  constpthread_mutexattr_t*restrictattr);/*初始化互斥量*/intpthread_mutex_destroy(pthread_mutex_t*mutex);/*销毁互斥量*/intpthread_mutex_lock(pthread_mutex_t*mutex);intpthread_mutex_trylock(pthread_mutex_t*mutex);intpthread_mutex_unlock(pthread_mutex_t*mutex);
  用互斥解决上面计算结果错误的问题,示例如下:#include<stdio.h>#include<string.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#defineLEN100000intnum=0;void*thread_func(void*arg){pthread_mutex_t*p_mutex=(pthread_mutex_t*)arg;for(inti=0;i<LEN;++i){  pthread_mutex_lock(p_mutex);  num+=1;  pthread_mutex_unlock(p_mutex);  }  returnNULL;  }intmain(){pthread_mutex_tm_mutex;  pthread_mutex_init(&m_mutex,NULL);pthread_ttid1,tid2;  pthread_create(&tid1,NULL,(void*)thread_func,(void*)&m_mutex);  pthread_create(&tid2,NULL,(void*)thread_func,(void*)&m_mutex);    pthread_join(tid1,NULL);  pthread_join(tid2,NULL);    pthread_mutex_destroy(&m_mutex);printf("correctresult=%d,result=%d.\n",2*LEN,num);return0;  }
  运行结果:correct result=200000, result=200000.
  如果在互斥中还嵌套有其他互斥代码,需要注意死锁问题。
  产生死锁的两种情况:
  一种情况是:如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁,然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,因此就永远处于挂起等待状态了,产生死锁。
  另一种典型的死锁情形是:线程A获得了锁1,线程B获得了锁2,这时线程A调用lock试图获得锁2,结果是需要挂起等待线程B释放锁2,而这时线程B也调用lock试图获得锁1,结果是需要挂起等待线程A释放锁1,于是线程A和B都永远处于挂起状态了。
  如何避免死锁:
  不用互斥锁(这个很多时候很难办到)
  写程序时应该尽量避免同时获得多个锁。
  如果一定有必要这么做,则有一个原则:如果所有线程在需要多个锁时都按相同的先后顺序(常见的是按Mutex变量的地址顺序)获得锁,则不会出现死锁。 (比如一个程序中用到锁1、锁2、锁3,它们所对应的Mutex变量的地址是锁1<锁2<锁3,那么所有线程在需要同时获得2个或3个锁时都应该按锁1、锁2、锁3的顺序获得。如果要为所有的锁确定一个先后顺序比较困难,则应该尽量使用pthread_mutex_trylock调用代替pthread_mutex_lock调用,以避免死锁。)【2】条件变量
  条件变量概括起来就是:一个线程需要等某个条件成立(而这个条件是由其他线程决定的)才能继续往下执行,现在这个条件不成立,线程就阻塞等待,等到其他线程在执行过程中使这个条件成立了,就唤醒线程继续执行。
  相关函数如下:#include<pthread.h>intpthread_cond_destroy(pthread_cond_t*cond);intpthread_cond_init(pthread_cond_t*restrictcond,constpthread_condattr_t*restrictattr);intpthread_cond_timedwait(pthread_cond_t*restrictcond,pthread_mutex_t*restrictmutex,conststructtimespec*restrictabstime);intpthread_cond_wait(pthread_cond_t*restrictcond,pthread_mutex_t*restrictmutex);intpthread_cond_broadcast(pthread_cond_t*cond);intpthread_cond_signal(pthread_cond_t*cond);
  举个最容易理解条件变量的例子,"生产者-消费者"模式中,生产者线程向队列中发送数据,消费者线程从队列中取数据,当消费者线程的处理速度大于生产者线程时,会产生队列中没有数据了,一种处理办法是等待一段时间再次"轮询",但这种处理方式不太好,你不知道应该等多久,这时候条件变量可以很好的解决这个问题。下面是代码:#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<stdio.h>#include<pthread.h>#include<errno.h>#include<string.h>#defineLIMIT1000structdata{  intn;structdata*next;};pthread_cond_tcondv=PTHREAD_COND_INITIALIZER;pthread_mutex_tmlock=PTHREAD_MUTEX_INITIALIZER;  structdata*phead=NULL;voidproducer(void*arg){printf("producerthreadrunning.\n");intcount=0;for(;;){intn=rand()%100;structdata*nd=(structdata*)malloc(sizeof(structdata));  nd->n=n;    pthread_mutex_lock(&mlock);structdata*tmp=phead;  phead=nd;  nd->next=tmp;  pthread_mutex_unlock(&mlock);  pthread_cond_signal(&condv);    count+=n;if(count>LIMIT){break;  }  sleep(rand()%5);  }printf("producercount=%d\n",count);  }voidconsumer(void*arg){printf("consumerthreadrunning.\n");intcount=0;for(;;){  pthread_mutex_lock(&mlock);if(NULL==phead){  pthread_cond_wait(&condv,&mlock);  }else{while(phead!=NULL){  count+=phead->n;structdata*tmp=phead;  phead=phead->next;free(tmp);  }  }  pthread_mutex_unlock(&mlock);if(count>LIMIT)break;  }printf("consumercount=%d\n",count);  }intmain(){pthread_ttid1,tid2;  pthread_create(&tid1,NULL,(void*)producer,NULL);  pthread_create(&tid2,NULL,(void*)consumer,NULL);    pthread_join(tid1,NULL);  pthread_join(tid2,NULL);return0;  }
  分享更多关于 Linux后端开发网络底层原理知识学习提升 点击正在跳转获取,完善技术栈,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。
  完整视频链接点击:C/C++Linux服务器开发/后台架构师【零声学院】-学习视频教程-腾讯课堂
  条件变量中的执行逻辑:
  关键是理解执行到int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) 这里时发生了什么,其他的都比较容易理解。执行这条函数前需要先获取互斥锁,判断条件是否满足,如果满足执行条件,则继续向下执行后释放锁;如果判断不满足执行条件,则释放锁,线程阻塞在这里,一直等到其他线程通知执行条件满足,唤醒线程,再次加锁,向下执行后释放锁。(简而言之就是:释放锁-->阻塞等待-->唤醒后加锁返回)
  上面的例子可能有些繁琐,下面的这个代码示例则更为简洁:#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<stdio.h>#include<pthread.h>#include<errno.h>#include<string.h>#defineNUM3pthread_cond_tcondv=PTHREAD_COND_INITIALIZER;pthread_mutex_tmlock=PTHREAD_MUTEX_INITIALIZER;    voidproducer(void*arg){intn=NUM;while(n--){  sleep(1);  pthread_cond_signal(&condv);printf("producerthreadsendnotifysignal.%d\t",NUM-n);  }  }voidconsumer(void*arg){intn=0;while(1){  pthread_cond_wait(&condv,&mlock);printf("recvproducerthreadnotifysignal.%d\n",++n);if(NUM==n){break;  }  }  }intmain(){pthread_ttid1,tid2;  pthread_create(&tid1,NULL,(void*)producer,NULL);  pthread_create(&tid2,NULL,(void*)consumer,NULL);    pthread_join(tid1,NULL);  pthread_join(tid2,NULL);return0;  }
  运行结果:producerthreadsendnotifysignal.1recvproducerthreadnotifysignal.1producerthreadsendnotifysignal.2recvproducerthreadnotifysignal.2producerthreadsendnotifysignal.3recvproducerthreadnotifysignal.3【3】信号量
  信号量适用于控制一个仅支持有限个用户的共享资源。用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该semaphore对象的等待时,该计数值减一;当线程完成一次对semaphore对象的释放时,计数值加一。当计数值为0时,线程挂起等待,直到计数值超过0.
  主要函数如下:#include<semaphore.h>intsem_init(sem_t*sem,intpshared,unsignedintvalue);intsem_wait(sem_t*sem);intsem_trywait(sem_t*sem);intsem_post(sem_t*sem);intsem_destroy(sem_t*sem);
  代码示例如下:#include<sys/types.h>#include<unistd.h>#include<stdlib.h>#include<stdio.h>#include<pthread.h>#include<errno.h>#include<string.h>#include<semaphore.h>#defineNUM5intqueue[NUM];sem_tpsem,csem;    voidproducer(void*arg){intpos=0;intnum,count=0;for(inti=0;i<12;++i){  num=rand()%100;  count+=num;  sem_wait(&psem);queue[pos]=num;  sem_post(&csem);printf("producer:%d\n",num);  pos=(pos+1)%NUM;  sleep(rand()%2);  }printf("producercount=%d\n",count);  }voidconsumer(void*arg){intpos=0;intnum,count=0;for(inti=0;i<12;++i){  sem_wait(&csem);  num=queue[pos];  sem_post(&psem);printf("consumer:%d\n",num);  count+=num;  pos=(pos+1)%NUM;  sleep(rand()%3);  }printf("consumercount=%d\n",count);  }    intmain(){  sem_init(&psem,0,NUM);  sem_init(&csem,0,0);pthread_ttid[2];  pthread_create(&tid[0],NULL,(void*)producer,NULL);  pthread_create(&tid[1],NULL,(void*)consumer,NULL);  pthread_join(tid[0],NULL);  pthread_join(tid[1],NULL);  sem_destroy(&psem);  sem_destroy(&csem);return0;  }
  信号量的执行逻辑:
  当需要获取共享资源时,先检查信号量,如果值大于0,则值减1,访问共享资源,访问结束后,值加1,如果发现有被该信号量挂起的线程,则唤醒其中一个线程;如果检查到信号量为0,则挂起等待。三、多线程编程总结与思考
  最后,我们对多线程编程进行总结与思考。
  第一点就是在进行多线程编程时一定注意考虑同步的问题,因为多数情况下我们创建多线程的目的是让他们协同工作,如果不进行同步,可能会出现问题。
  第二点,死锁的问题。在多个线程访问多个临界资源时,处理不当会发生死锁。如果遇到编译通过,运行时卡住了,有可能是发生死锁了,可以先思考一下是那些线程会访问多个临界资源,这样查找问题会快一些。
  第三点,临界资源的处理,多线程出现问题,很大原因是多个线程访问临界资源时的问题,一种处理方式是将对临界资源的访问与处理全部放到一个线程中,用这个线程服务其他线程的请求,这样只有一个线程访问临界资源就会解决很多问题。
  第四点,线程池,在处理大量短任务时,我们可以先创建好一个线程池,线程池中的线程不断从任务队列中取任务执行,这样就不用大量创建线程与销毁线程,这里不再细述。

为什么会全身酸痛(女性全身肌肉疼痛是什么病)福州新闻网10月14日讯(福州晚报记者朱丹华通讯员陈钰涛)感觉浑身上下哪都疼,可影像学检查未见异常吃止痛药也没效果,41岁的林老师多年来饱受怪病折磨。近日,在福建省立医院疼痛科门诊小腹疼痛是什么原因?(小腹疼痛的原因有哪些?)小腹疼痛是什么原因?(小腹疼痛的原因有哪些?)因为身体构造的特殊性,女性出现小腹痛的几率远高于男性。女性小腹疼痛的原因有很多,偶有疼疼不必过于紧张,若长期性疼痛,则一定要引起重视。小腹疼痛的原因(小腹疼痛的原因有哪些?)小腹疼痛的原因(小腹疼痛的原因有哪些?)因为身体构造的特殊性,女性出现小腹痛的几率远高于男性。女性小腹疼痛的原因有很多,偶有疼疼不必过于紧张,若长期性疼痛,则一定要引起重视。除了外小腹疼痛的原因(女性小腹痛就是痛经?)小腹疼痛的原因(女性小腹痛就是痛经?)很多女孩子都有小腹坠痛的经历,如果在稍作休息后就有所缓解就往往会被忽略。事实上很很多疾病的症状都可能造成小腹坠痛。因此当出现小腹坠痛时一定不要后腰痛的原因有哪些(后腰中间疼痛是什么原因)小张今年20岁出头,最近半年却一直被腰疼所困扰,特别是后半夜,时常会被疼醒。早上起床时,腰背部也是疼痛僵硬。可令他费解的是,只要出门上班活动一下,症状反而有所缓解。小张觉得可能是工小腹是哪里(女生小腹下面三角区疼痛怎么回事)女性因为生理周期,出现小腹疼痛是很常见的事情,但也并非所有疼痛都是因为生理周期引起那么女生引起女生小腹疼的原因到底有哪些呢?又该如何治疗和预防呢?下面我们就一起来了解一下。痛经痛经感冒为什么腰酸(感冒后腰背酸疼因为什么)感冒对于很多人而言并不陌生,有的人感冒只是单纯的咳嗽流鼻涕,有的人可能还有全身症状,比如出现浑身疼痛现象。广州中医药大学金沙洲医院疼痛科首席专家王家双教授提到,感冒引起浑身酸痛,一为什么老是腰酸(两边腰酸胀跟肾有关系吗)在当今社会中,由于长时间坐着以及来自生活上方方面面的压力,导致许多人很容易感觉自身随便动一动就腰酸背痛。轻者或许只有感觉到腰酸背痛,严重者很有可能出现一些耳鸣等其他症状。那么,这究为什么腰酸胀(腰酸胀很累是什么原因)经常做体力活的人,可能总是会感觉自己腰酸背痛的浑身没力气上班一直坐着不动,也会感觉到腰部酸胀,腰疼的时候用手捶几下就好了劳累过度的时候多休息休息就能减轻了。相比很多人都是这种想法吧为什么我腰酸(为什么我会觉得腰酸很累)女人,你仔细想过这个问题吗?很多女人一定都没想过,很多女人也一定认为,只有自己老公才需要补肾,自己不需要其实,这个想法是大错特错的。补肾,无关男女,只要有肾虚,就得补肾。不管是男人背痛是什么原因(哪种背痛是肿瘤引起的)背部是人体的一大支撑,但是有时候背部也会出现一些异常的现象,有部分人出现了严重的背痛症状,背部一旦疼痛的话,平时还可能会出现腰间盘突出等各种情况。有些人指出可能背部疼痛是由于肺癌引
蟹哪里好吃(哪里的海蟹好吃)秋冬季节的螃蟹,是最肥美的。什么季节,吃什么美食。这个时候,各地产的螃蟹都隆重登场,引得无数食客垂涎。说起大闸蟹,那可是无人不知,家喻户晓。特别是阳澄湖大闸蟹,更是火遍大江南北。俗哪里烤串好吃(哪里的火烧最好吃最有名)烧烤在今天的中国几乎是一项全民运动,试问有几个人能抗拒烧烤的魅力呢?全国各地哪儿的烧烤最好吃?今天,小编就用满满干货给大家介绍全国各地的烧烤吧!一东北烧烤没有什么事是一顿烧烤解决不哪里有螃蟹(中国蟹都是哪)喜欢吃螃蟹的朋友,应该听过这么2句品蟹俗语,秋风起,蟹脚肥,九月圆脐十月尖,9月尝鲜,10月品黄,11月吃膏。每年的9月到11月,都是吃螃蟹的好时节。这个季节的螃蟹,膏多肉肥,吃起人体有哪些组织(人体有哪5大部分组成)提到垃圾,我们的第一反应通常是无用的东西。而在我们的体内,有一些DNA,也被称为垃圾。那么,这些被称为垃圾DNA的,真的是我们体内无用的东西吗?近期,一项发表于细胞干细胞的研究发现降血脂药(15种降血脂的中成药)降血脂药(15种降血脂的中成药)今天更新高脂血症的第二个中医证型气滞血瘀证可用的中成药气滞血瘀证的主要症状表现胸胁脘腹胀闷窜痛,偶有刺痛,或体内有痞块,时聚时散,舌质暗有瘀点或瘀斑哪些姓好听(小众好听的姓氏)一龙姓源流古老涉及上古神话传说,象征着高贵的身体地位,这个姓氏一眼就令人难忘。二风姓这个姓氏很有仙侠气息,中国上古三皇五帝之首伏羲氏的父亲燧人氏就是风姓。用这个姓氏起名字既好听又飘纪录片川岛芳子生死大揭秘4(川岛芳子生死之谜解密第5集)关于川岛芳子之死,向来存在着很多的争议,当时有人声称被枪毙的不是川岛芳子,而是另有其人,而川岛芳子则是化名方姥,依然好好的活在人世。可是网上也有人认为方姥就是庄桂贤这一点铁证无疑,湘是哪个省的简称(两湘是指湖南湖北吗)湖南省,简称湘,省会长沙。一提到长沙,我们就想到了臭豆腐,的确,长沙臭豆腐闻名全国,是湖南省的一个特色小吃,但是关于湖南省的其他知识,你知道多少呢?1湖南省不是所有的城市都位于长江徐汇是哪里(上海市徐汇区繁华吗)这里是刘小顺的旅行和生活研究所。上海是我国最繁华最热闹的国际化大都市之一,商业非常发达,因此有许多著名的商圈,比如大家都很熟悉的人民广场陆家嘴静安寺等等。不过,说到上海市区的著名商親家四姊妹全集漫画韩漫阅读亲家四姐妹咏芯资源下文自取(亲家四姐妹)的漫画哪里看?徽加好友搜索到1425739638(不是供重浩)简介原來除了我的暗戀對象,嫂嫂的妹妹們也跟我哥有染?!在這不可告人的不倫關係中,我們該如何選擇?8。98美国双头人(美国双头人姐妹现状)美国双头人(美国双头人姐妹现状)几成桑田成沧海,又逢枯木两生花。传说中有一种花名为双生花,一茎分支并开两花,两花不断地吸取对方的营养供自己存活,直到其中一朵枯萎死去。在美国明尼苏达