当进程为执行任务而多次切换时,必然会产生额外的开销。这时引入一个更小的单位——线程。
一个进程里面有多个线程,这些线程也可能会服务于其他进程。
例如酷狗播放器的声音处理线程,与爱奇艺的声音处理线程可能为同一线程。
线程共享进程的所有信息。
线程共享的进程环境包括:
- 进程代码段
- 进程的公有资源(如全局变量,利用这些共享的数据,线程很容易的实现相互之间的通信)
- 进程打开的文件描述符
- 消息队列
- 信号的处理器
- 进程的当前目录
- 进程用户ID
- 进程组ID
线程独占资源:
- 线程ID
- 寄存器组的值
- 用户栈、内核栈(在一个进程的线程共享堆区(heap))
- 错误返回码
- 线程的信号屏蔽码
- 线程的优先级
同一个进程的不同线程,哪个是不能共享的?
A全局变量
B栈
C堆
D文件句柄
答案是B
进程创建 pthread_create();
等待结束:pthread_join
子线程结束:pthread_exit
#include<pthread.h>
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<string.h>
#define Eerr_exit(m)\
do\
{\
perror(m);\
return(EXIT_FAILURE);\
}while(0)
using namespace std;
void * thr_fn(void *arg)
{
cout<<"thread 1 running!"<<endl;
return ((void*)1);
}
void *thr_fn2(void *arg)
{
cout<<"thread exiting"<<endl;
pthread_exit ((void*)2);
}
int main(void)
{
int err;
pthread_t tid1,tid2;
void *tret;
err=pthread_create(&tid1,NULL,thr_fn,NULL);
if(err!=0)
{
cout<<"can't create thread 1"<<endl;
exit(0);
}
err=pthread_create(&tid2,NULL,thr_fn2,NULL);
if(err!=0)
{
cout<<"can't create thread 2"<<endl;
exit(0);
}
err=pthread_join(tid1,&tret);
if(err!=0)
{
cout<<"can't join with thread 1"<<endl;
exit(0);
}
cout<<"thread 1 exit with code"<<tret<<endl;
err=pthread_join(tid2,&tret);
if(err!=0)
{
cout<<"can't join with thread 2"<<endl;
exit(0);
}
cout<<"pthread 2 exit with code"<<tret<<endl;
exit(0);
}
多线程同步问题:
因为多线程执行的随机性,我们要设法使其同步才不至于出现幻影,脏数据等现象,这一点与数据库颇有异曲同工之妙。
1.互斥锁:
互斥锁有pthread_mutex_lock加锁,和pthread_mutex_unulock解锁两种状态来保证同步
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
int num=20;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void *sell_ticket(void *arg)
{
for(int i=0;i<20;i++)
{ pthread_mutex_lock(&mutex);
if(num>0)
{
sleep(1);
// cout<<"sell one ticket!"<<"the num is: "<<20-num+1<<endl;
printf("the %dth be selled \n",20-num+1);
--num;
}
pthread_mutex_unlock(&mutex);
}
return 0;
}
int main()
{
pthread_t pid[4];
int ret;
for(int i=0;i<4;i++)
{
ret=pthread_create(&pid[i],NULL,&sell_ticket,NULL);
if(ret)
{
cout<<"create pthread error!"<<endl;
return ret;
}
}
sleep(10);
void *retval;
for(int i=0;i<4;i++)
{
ret=pthread_join(pid[i],&retval);
if(ret)
{
cout<<"pthread join error: "<<ret<<endl;
return ret;
}
cout<<"retval="<<retval<<endl;
}
return 0;
}
1.(2)互斥锁还有一个pthread_mutex_trylock方法,当其发现锁被占据,则返回EBUSY而非等待,这是他与pthread_mutex_lock不同的地方
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
int num=20;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void *sell_ticket(void *arg)
{
for(int i=0;i<20;i++)
{ pthread_mutex_lock(&mutex);
if(num>0)
{
sleep(1);
// cout<<"sell one ticket!"<<"the num is: "<<20-num+1<<endl;
printf("the %dth be selled \n",20-num+1);
--num;
}
pthread_mutex_unlock(&mutex);
}
return 0;
}
void *sell_ticket2(void *arg)
{
for(int i=0;i<20;i++)
{ int ret=pthread_mutex_trylock(&mutex);
if(ret==EBUSY)
{ cout<<"the varible is locked by sell_1";
printf("\n");
}
else if(ret==0)
if(num>0)
{
sleep(1);
// cout<<"sell one ticket!"<<"the num is: "<<20-num+1<<endl;
printf("the %dth be selled \n",20-num+1);
--num;
}
pthread_mutex_unlock(&mutex);
}
return 0;
}
int main()
{
pthread_t pid[2];
int ret;
ret=pthread_create(&pid[0],NULL,&sell_ticket,NULL);
if(ret)
{
cout<<"create pthread error!"<<endl;
return ret;
}
ret=pthread_create(&pid[1],NULL,&sell_ticket2,NULL);
if(ret)
{
cout<<"create pthread error!"<<endl;
return ret;
}
sleep(10);
void *retval;
ret=pthread_join(pid[0],&retval);
if(ret)
{
cout<<"pthread join error: "<<ret<<endl;
return ret;
}
else
cout<<"retval="<<retval<<endl;
ret=pthread_join(pid[1],&retval);
if(ret)
{
cout<<"pthread join error: "<<ret<<endl;
return ret;
}
else
cout<<"retval="<<retval<<endl;
return 0;
}
2.条件变量
互斥锁在加锁的时候,重复的检查与阻塞导致资源的浪费,这时引入了条件变量。
在条件不满足时使线程睡眠,满足条件时激活线程,可以说非常理想了。
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
pthread_cond_t qready=PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock=PTHREAD_MUTEX_INITIALIZER;
int x=0,y=20;
void *func1(void *arg)
{
cout<<"func1 start"<<endl;
pthread_mutex_lock(&qlock);
while(x<y)
pthread_cond_wait(&qready,&qlock);
pthread_mutex_unlock(&qlock);
sleep(3);
cout<<"ufncl end"<<endl;
}
void *func2(void *arg)
{
cout<<"func2 start"<<endl;
pthread_mutex_lock(&qlock);
x=20,y=10;
cout<<"has change x and y"<<endl;
pthread_mutex_unlock(&qlock);
if(x>y)
pthread_cond_signal(&qready);
cout<<"func2 end"<<endl;
}
int main()
{
pthread_t tid1,tid2;
int iret;
iret=pthread_create(&tid1,NULL,func1,NULL);
if(iret)
{
cout<<"pthread 1 create error"<<endl;
return iret;
}
sleep(2);
iret=pthread_create(&tid2,NULL,func2,NULL);
if(iret)
{
cout<<"pthread 2 create error"<<endl;
return iret;
}
sleep(5);
return 0;
}
3.读写锁
写锁被占据时,则其线程不能加锁。当读锁被占据时,其他线程也可以加读锁,也就是有读权限。
#include<iostream>
#include<unistd.h>
#include<pthread.h>
using namespace std;
#define THREADNUM 5
pthread_rwlock_t rwlock;
void *readers(void *arg)
{
pthread_rwlock_rdlock(&rwlock);
cout<<"readers "<<arg<<" got the lock"<<endl;
pthread_rwlock_unlock(&rwlock);
pthread_exit((void *)0);
}
void *writers(void *arg)
{
pthread_rwlock_wrlock(&rwlock);
cout<<"writers "<<arg<<" get the lock"<<endl;
pthread_rwlock_unlock(&rwlock);
pthread_exit((void*)0);
}
int main()
{
int ret,i;
pthread_t wr,rd;
int wrNum=0,rdNum=0;
pthread_attr_t attr;
ret=pthread_rwlock_init(&rwlock,NULL);
if(ret)
{
cout<<"init lock failured"<<endl;
return ret;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
for(i=0;i<THREADNUM;i++)
{
if(i%3)
{
pthread_create(&rd,&attr,readers,(void*)rdNum);
cout<<"create readers "<<rdNum++<<endl;
}
else
{
pthread_create(&wr,&attr,writers,(void*)wrNum);
cout<<"create writers "<<wrNum++<<endl;
}
}
sleep(5);
return 0;
}
4.信号量。与互斥锁类似,但是允许多个线程进入临界区。
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
using namespace std;
#define CUSTOMER_NUM 10
sem_t sem;
void *get_service(void *pthread_id)
{
int customer_id=*((int*)pthread_id);
if(sem_wait(&sem)==0)
{
usleep(100);
cout<<"customer "<<customer_id<<" receive service"<<endl;
sem_post(&sem);
}
}
int main()
{
sem_init(&sem,0,2);
pthread_t customer[CUSTOMER_NUM];
int i,ret;
for(i=0;i<CUSTOMER_NUM;i++)
{
int customer_id=i;
ret=pthread_create(&customer[i],NULL,get_service,&customer_id);
if(ret)
{
cout<<"create pthread failured"<<endl;
return ret;
}
else
cout<<"customer "<<i<<" arrived"<<endl;
usleep(10);
}
int j;
for(j=0;j<CUSTOMER_NUM;j++)
pthread_join(customer[j],NULL);
sem_destroy(&sem);
return 0;
}