多线程
进程和线程的区别
- 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1至n个线程。
- 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
- 线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
- 多进程是指操作系统能同时运行多个任务(程序)。
- 多线程是指在同一程序中有多个顺序流在执行。
多进程进程存在的问题,多线程的好处
- 进程间的调度会产生竞争CPU、内存等系统资源,而线程并不拥有资源,不涉及资源和CPU的竞争
- 多线程可支持同一个应用程序内部的并发
- 免去进程间的频繁切换的开销
- 并发也变得简单
- 线程的切换是轻量级的,保证切换的速度
- 可以及时响应事件状态的变化
- 线程计算能力不大
- 对于高并发的网络是先天适用的
什么是多线程?
与我们平时见的一样,程序的运行过程只有一个控制权的存在,函数是逐个进行,按照调度的顺序向下走,多线程允许一个进程中存在多个控制权,多个函数的操作可以同时进行,达到高并发,不用等待进行阻塞。
多线程主要的问题
抢夺资源,引起线程安全问题
解决方案:锁(mutex、条件变量、读写锁)
多线程中每个线程拥有独立的栈,why?
栈在内存中的调度是不断把栈底部的变量或者函数进行激活,进行调度,如果多线程共用一个栈,那么多个线程会不停地在切换,不停的再进行压栈和弹栈的操作,严重限制多线程。
一个栈的大小一般为8m,主要可以通过命令==ulimit -s==得到操作系统的栈大小
==每个线程间的栈是独立的==,并且多个栈之间一定的空白区域隔开,已被栈的增长。==线程之间共享文本、堆和全局变量==。
pthread_detach() 详细讲解线程结合和分离
分离线程,对线程的终止状态不感兴趣,对线程进行分离,让操作系统在线程退出时收回它占用的资源
另外一种替代的方法是在创建线程开始时就修改pthread_attr_t的detachstate属性,进行分离
线程API http://www.cnblogs.com/kuliuheng/p/4062941.html
gcc/g++ program.o -o program -lpthread
实例1-购买火车票(额定火车票数量,多用户购买,利用互斥量)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h> //linux下的线程库
#include <errno.h>
pthread_mutex_t mutex_x= PTHREAD_MUTEX_INITIALIZER;
int total_ticket_num=20;
void *sell_ticket1(void *arg){
for(int i=0;i<20;i++){
pthread_mutex_lock(&mutex_x);
if(total_ticket_num>0){
printf("thread1 sell the %dth ticket\n",20-total_ticket_num+1);
total_ticket_num--;
}
sleep(1);
pthread_mutex_unlock(&mutex_x);
sleep(1);
}
return 0;
}
void *sell_ticket2(void *arg){
int iRet=0;
for(int i=0;i<10;i++){
iRet=pthread_mutex_trylock(&mutex_x); //试图访问锁,如果检测到锁被占据时返回EBUSY
if(iRet==EBUSY){
printf ("sell_ticket2:the variable is locked by sell_ticket1.\n");
}else if(iRet==0){
if(total_ticket_num>0){
printf("thread2 sell the %dth ticket\n",20-total_ticket_num+1);
total_ticket_num--;
}
pthread_mutex_unlock(&mutex_x);
}
sleep(1);
}
return 0;
}
//创建两个线程进行购票
int main(){
pthread_t tids[2];
int iRet = pthread_create(&tids[0], NULL, &sell_ticket1, NULL);
if(iRet){
printf("pthread_create error, iRet=%d\n",iRet);
return iRet;
}
iRet = pthread_create(&tids[1], NULL, &sell_ticket2, NULL);
if(iRet){
printf("pthread_create error, iRet=%d\n",iRet);
return iRet;
}
sleep(30);
void *retval;
//等待线程1结束
iRet=pthread_join(tids[0], &retval);
if(iRet){
printf("tid=%d join error, iRet=%d\n",tids[0],iRet);
}else{
printf("retval=%ld\n",(long*)retval);
}
//等待线程2结束
iRet=pthread_join(tids[1], &retval);
if(iRet){
printf("tid=%d join error, iRet=%d\n",tids[1],iRet);
}else{
printf("retval=%ld\n",(long*)retval);
}
return 0;
}
==条件变量和互斥锁== (条件变量特别适用于多个线程等待某个事件的发生,如果不使用条件变量,每个线程就需要不断尝试去获得互斥锁并检查条件是否发生)
- 为什么引入条件变量?
单独使用互斥锁可能会引起线程对共享数据结构或变量的重复性检查,浪费时间和资源。因此引入条件变量,当线程在等待某些条件时线程进入睡眠状态,一旦条件满足,就进行唤醒因等到满足特定条件而睡眠的线程。
简单来说,对线程设定一个flag,满足这个flag唤醒或者等待,不满足条件时,线程往往解开相应的互斥锁并等待条件发生变化,满足条件后就进行唤醒被阻塞的线程。
注意 适用条件变量时一定要掌握好触发的时机,时机不对可能引起空等待
读写锁(多读少写)和信号量(允许多个线程进入临界区同时操作,sem_t)
实例2-出租车,出租来了等人,人来了有车上车没车等车
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <iostream>
#include <pthread.h>
using namespace std;
/*提示出租车到达的条件变量*/
pthread_cond_t taxiCond = PTHREAD_COND_INITIALIZER;
/*同步锁*/
pthread_mutex_t taxiMutex = PTHREAD_MUTEX_INITIALIZER;
int travelerCound=0;
//乘客来了,先加锁,防止多个线程请求pthread_cond_wait产生竞争,然后进行等待,如果有车不等待直接走,释放所,没有就阻塞继续等待
void * traveler_arrive(void * name){
cout<<"Traveler: "<<(char *)name<<" needs a taxi now!"<<endl;
pthread_mutex_lock(&taxiMutex);
travelerCound++;
pthread_cond_wait(&taxiCond,&taxiMutex);
pthread_mutex_unlock(&taxiMutex);
cout<<"Traveler: "<<(char *)name<<" now got a taxi!"<<endl;
pthread_exit((void*)0);
}
//车来了,通知阻塞的乘客来上车,如果没有乘客直接成功返回
void * taxi_arrive(void * name){
cout<<"Taxi: "<<(char *)name<<" arrives."<<endl;
while(1){
pthread_mutex_lock(&taxiMutex);
if(travelerCound>0){
pthread_cond_signal(&taxiCond);
pthread_mutex_unlock(&taxiMutex);
break;
}
pthread_mutex_unlock(&taxiMutex);
}
pthread_exit((void*)0);
}
int main(){
pthread_t tids[3];
int iRet = pthread_create(&tids[0],NULL,taxi_arrive,(void*)(" Jack "));
if(iRet){
printf("pthread_create error: iRet=%d\n",iRet);
return iRet;
}
printf("Time passing by.\n");
sleep(1);
iRet = pthread_create(&tids[1],NULL,traveler_arrive,(void*)(" Susan "));
if(iRet){
printf("pthread_create error: iRet=%d\n",iRet);
return iRet;
}
printf("Time passing by.\n");
sleep(1);
iRet = pthread_create(&tids[2],NULL,taxi_arrive,(void*)(" Mike "));
if(iRet){
printf("pthread_create error: iRet=%d\n",iRet);
return iRet;
}
printf("Time passing by.\n");
sleep(1);
void *retval;
for(int i=0;i<3;i++){
iRet=pthread_join(tids[i],&retval);
if (iRet){
printf("pthread_join error: iRet=%d\n",iRet);
return iRet;
}
printf("retval=%ld\n",(long)retval);
}
return 0;
}