因为内核为每个线程都开辟了独立空间(task_struct),所以进程间互不影响,但切换时系统开销太大,所以引入了一个轻量级进程(LWP),也就是线程了。
线程共享地址空间,但因为Linux本事是不区分进程和线程的,所以要通过一系列的命令来达成我们的目的
多线程的好处: 提高任务切换效率;
避免额外TLB&cache刷新;
线程共享资源:可执行的命令、静态数据、进程打开的文件描述符、当前工作目录、用户ID、用户组ID
线程私有资源:线程ID(TID),PC(程序计数器)和相关寄存器、堆栈、错误号、优先级、执行状态和属性
线程库
创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*touyine)(void *),void *arg);
返回值:成功:0,失败:错误码
thread:线程对象
attr:线程属性,NULL为默认属性
routine:线程要执行的函数
arg:传给函数的参数
回收线程
#include <pthread.h>
int pthread_join(pthread_t thread,void **retval);
返回值:成功:0,失败:错误码
thtead:线程回收对象
*retval:用来接收线程thread的返回值
此函数调用线程一直阻塞到thread结束
结束线程
#include <pthread.h>
void pthread_exit(void *retval)
没有返回值;
线程私有资源被释放
retval可以被其他线程通过回收(pthread_jion)获取
示例:
char msg[32] = "hello world";
void *thread_func(void *arg);
int main(void)
{
pthread_t a_thread;
void *result;
if(pthread_create(&a_thread,NULL,thread_func,NULL) != 0)
{ //以线程对象a_thread创建线程,函数thread_func
printf("fail create\n");
exit(-1);
}
pthread_join(a_thread,&result);
//回收线程a_thread,将结果存入地址result
printf("result is %s\n",result);
printf("message is %s\n",message);
return 0;
}
void *thread_func(void *arg)
{
sleep(1);
strcpy(msg,"maked by thread");
pthread_exit("thank you")
}
/*
运行结果:result is thank you
message is maked by thread
以上示例具体流程:1.创建线程并执行函数(thread_func)
2.在函数中在msg中存入字符串,在*retval中存入”thank you“
3.线程回收,result为线程结束后*retval被pthread_join获取并存入的地址
4.msg为在函数中存入的字符串
*/
线程的同步与互斥
知道了线程创建,结束,回收的流程后,便要考虑线程间如何进行通信。由于共享地址空间,所以通信很容易,只需要通过全局变量来交换数据便可。但是因为有多个线程访问共享机制往往造成混乱,所以引入了同步或互斥机制。
同步便是指多个任务按照先后次序依次配合完成一件事。可是如何排序?这时候在1968年,E.D便引入了信号量的概念,由信号量决定线程继续运行或是阻塞。
互斥是指一次指允许一个任务(进程、线程)访问共享资源,由此引入了临界资源,互斥锁等概念
同步
信号量代表一类资源,根据有没有信号量来决定线程是否运行,就像派工单一样,拿到了才可以干活,干完活了再还回来。
所以具体流程就是:
初始化;
P操作:申请资源;(领牌子)
V操作:释放资源;(还牌子)
int sem_init(sem_t *sem,int pshared,unsigned int value);
返回值:成功:0;失败:EOF
sem:初始化的信号量对象
psharde:线程间:0,进程间:1
信号量初值:有几个牌子。
int sem_wait(sem_t *sem); //申请
返回值:成功:0;失败:EOF
sem:信号量对象
int sem_post(sem_t *sem); //释放
返回值:成功:0;失败:EOF
sem:信号量对象
编译时添加 -lpthread
示例1,读写一个信号量:
char buf[32];
sem_t sem; //定义全局变量
void *function(void *arg);
int main(void)
{
pthread_t a_thread;
if(sem_init(&sem,0,0) < 0) //1信号量初始化
{
perror("sem_init");
exit(-1);
}
if(pthread_create(&a_thread,NULL,function,NULL) != 0)
{ //2创建线程并执行函数
printf("failed create\n");
exit(-1);
}
printf("input 'quit' to exit\n");
do{
fgets(buf,32,stdin); //4键盘输入
sem_post(&sem); //5释放资源,还牌子
}while(strncmp(buf,"quit",4));
}
void *function(void *arg)
{
while(1)
{
sem_wait(&sem); //3申请资源
printf("you enter %d characters\n",strlen(buf));
}
}
示例2,读写两个信号量,框架与上例相同,只是多了信号量:
char buf[32]; //定义全局变量
sem_t sem_r,sem_w;
//定义读信号量和写信号量,不妨定义为一个红牌子(读),一个绿牌子(写)
void *function(void *arg);
int main(void)
{
pthread_t a_thread;
if(sem_init(&sem_r,0,0) < 0) //读信号量初始化,有1个红牌
{
perror("sem_init");
exit(-1);
}
if(sem_init(&sem_w,0,0) < 0) //写信号量初始化,有一个绿牌
{
perror("sem_init");
exit(-1);
}
if(pthread_create(&a_thread,NULL,function,NULL) != 0)
{ //创建线程并执行函数
printf("failed create\n");
exit(-1);
}
printf("input 'quit' to exit\n");
do{
sem_wait(&sem_w); //给绿牌,开始输入,(刚开始没有就不给了)
fgets(buf,32,stdin); //键盘输入
sem_post(&sem_r); //还红牌
}while(strncmp(buf,"quit",4));
return 0;
}
void *function(void *arg)
{
while(1)
{
sem_wait(&sem_r); //给红牌,告诉结果
printf("you enter %d characters\n",strlen(buf));
sem_post(&sem_w); //还绿牌,
}
}
互斥
定义: 临界资源:一次只允许一个任务(进程、线程)访问的共享资源。
临界区:访问临界资源的代码。
互斥机制:访问临界资源前申请锁,访问完后释放锁。
有定义看出,与信号量大致相同。既初始化,申请锁,释放锁。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
返回值:成功:0;失败:错误码
mutex:要初始化的互斥锁对象
attr:互斥锁属性,NULL表示缺省属性
int pthread_mutex_lock(pthread_mutex_t *mutex);
返回值:成功:0;失败:错误码
若无法获得锁,则任务阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功:0;失败:错误码
注意执行完临界区应立即释放锁
示例:
void *function(void *arg);
pthread_mutex_t lock;
unsigned int count,value1,value2;
int main(voiid)
{
pthread_t a_thread;
if(pthread_mutex_init(&lock,NULL) != 0)
{ //初始化锁
printf("fail\n");
exit(-1);
}
if(pthread_create(&a_thread,NULL,function,NULL) != 0)
{ //创建线程
printf("failed\n");
exit(-1);
}
while(1) //理论上value1 =value2
{
count++;
#ifdef _LOCK_
pthread_mutex_lock(&lock);
#endif
value1 =count;
value2 =count;
#ifdef _LOCK_
pthread_mutex_lock(&lock);
#endif
}
return 0;
}
void *function(void *arg)
{
while(1)
{
#ifdef _LOCK_
pthread_mutex_lock(&lock);
#endif
if(value1 != value2) //当不相等时答应,相等则不打印
{
printf("value1 = %u,value2 =%u\n",value1,value2);
usleep(100000);
}
#ifdef _LOCK_
pthread_mutex_lock(&lock);
#endif
}
return NULL;
}
示例程序的结果显示,当不加锁的时候总会有几个时刻value1与value2相差1点,加锁时则永远相等。