linux system pthread
原文链接: linux system pthread
一 什么是线程?
- 进程执行的最小单元是线程,被称为轻量级进程(LightWeight Process,LWP)
- 单线程的进程可以简单认为是只有一个线程的进程
- 一个进程在同一时间可以做一件事,有了多线程后一个进程同一时间做很多件事情
- 无论系统有几个cpu,多线程都可以使进程并发处理多个事务
- 一个线程阻塞并不会影响到另一个线程
- 多线程的进程可以尽可能的利用系统cpu系统
- 有几个线程就有几个栈空间
- 进程内所有信息对于线程都是共享的,包括执行代码.全局变量,堆栈内存以及文件描述符
二 线程的标识
- 线程有自己的ID
- 线程ID 用pthread_t 表示, 不能把pthread_t当整数处理
- 线程可以通过pthread_self()函数获得自身的线程ID
线程调用
1)线程创建
- 在进程中只有一个控制线程
- 程序开始运行的时候每个进程只有一个线程,它是以单线程方式启动
的,在创建多线程以前,进程的运行方式和传统无区别
- gcc在链接的时候需要增加 -pthread选项
- 创建一个线程调用pthread_created函数
#include <pthread.h>
int pthread_created (pthread_t * thread,const pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
//如果pthread返回成功,由参数thread指向的内存单元被设置为新创建进程的线程
//attr参数用于定制各种不同的线程属性
//新创建的线程从start_rtn函数地址开始执行,该函数只有一个
void *参数,如果需要向start_rtn函数传递多个参数.就需要把这些参数放到一个结构中
,然后把这个结构的地址作为void*传入
//线程创建的时候不能保证哪个先运行
//pthread函数成功返回0
//参数arg用来区别线程,或者生成好多相同的线程
/*创建线程*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *func(void *arg)//线程入口函数
{
return NULL;
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
pthread_t thrd;
if (pthread_created(&thrd,NULL,func,NULL) != 0)//四个参数两个必填
{
printf("error is %s",strerror(errno));
}
}
2)线程终止
- 任一线程调用了exit函数,整个进程就会终止
- 如果信号默认动作是终止进程,那么信号发送到该进程,整个进程也会被终止
- 单个线程通过以下三种方式退出:
1, 线程从启动函数中返回,返回值是退出线程码
2, 线程可以被同一进程中的其他线程取消
3, 线程调用pthread_exit(NULL);
void pthread_exit(void *arg)
//arg是个无类型的指针,该指针会被其他线程调用pthread_join捕捉
/*创建多个相同线程*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *func(void *arg)//线程入口函数
{
int *p = (int *)arg;
int i = *p;
free(p);
printf("pthread start %d\n",i);
while(1)
{
printf("pthread %d\n",i);
}
printf("pthread end %d\n",i);
return NULL;
}
void createthr()
{
pthread_t thrd1,thrd2;
int *i1 = malloc(sizeof(int));//暂时不能释放i1,所以在堆上分配内存
int *i2 = malloc(sizeof(int));//或者static int i2也可以
*i1 = 1; *i2 = 2;
if (pthread_created(&thrd1,NULL,func,i1) != 0)//四个参数两个必填
{
printf("error is %s",strerror(errno));
}
if (pthread_created(&thrd2,NULL,func,i2) != 0)
{
printf("error is %s",strerror(errno));
}
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
createthr();
while(1)
{
sleep(1);
}
return EXIT_SUCCESS;
}
3)线程捕获
int pthread_join (pthread_t th,void **thr_return)
//pthread_join函数用于挂起当前线程,直至th指点的线程终止为止
//如果一个线程的返回值不是NULL,则保存在thr_return地址中
//一个线程所使用的内存资源在应用pthread_join调用之前不会重新分配,
所以对于每个线程必须调用一次pthread_join函数
//其他线程不能对同一线程再应用pthread_join函数
/*创建多个相同线程,以及返回值的处理*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *test(void *arg)
{
int *a = malloc(sizeof(int));
*a = 100;
pthread_exit((void *)a);//或者return a;
}
void *func(void *arg)//线程入口函数
{
int *p = (int *)arg;
int i = *p;
free(p);
printf("pthread start %d\n",i);
printf("pthread %d\n",i);
printf("pthread end %d\n",i);
if(i == 2)
{
test();
}
return NULL;
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
pthread_t thrd1,thrd2;
int *i1 = malloc(sizeof(int));//暂时不能释放i1,所以在堆上分配内存
int *i2 = malloc(sizeof(int));//或者static int i2也可以
*i1 = 1; *i2 = 2;
if (pthread_created(&thrd1,NULL,func,i1) != 0)//四个参数两个必填
{
printf("error is %s",strerror(errno));
}
if (pthread_created(&thrd2,NULL,func,i2) != 0)
{
printf("error is %s",strerror(errno));
}
pthread_join(thrd1,NULL);//主线程挂起,等待thrd1退出
int *p = NULL;
pthread_join(thrd2,(void **)&p)//第二个参数取得返回值
printf("p = %d\n",*p);
printf("main end\n");
return EXIT_SUCCESS;
}
4)线程的被分离状态
int pthread_detach(pthread_t th)
//pthread_detach函数使得线程处于被分离状态
//对于被分离状态的线程,不需要调用pthread_join,如果对其他线程对其调用pthread_join
会失败,并返回EINVAL
//如果不等待一个线程,同时对线程的返回值不感兴趣,可以设置这个线程为被分离状态,
让系统在线程退出时候自动回收它所占用的资源
//一个线程不能自己调用pthread_detach改变自己为被分离状态,只能由其他线程调用
pthread_detach函数
/*设置可分离状态*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *func(void *arg)//线程入口函数
{
printf("pthread start \n");
printf("pthread end \n");
pthread_exit(NULL);
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
pthread_t thrd;
pthread_created(&thrd1,NULL,func,NULL)
pthread_detach(thrd);
sleep(1);
return EXIT_SUCCESS;
}
/*
一旦线程成为可分离线程之后,就不能再使用pthread_join了
可分离线程的使用场景
1、主线程不需要等待子线程
2、主线程不关心子线程的返回码
*/
5)在另一个进程中终止一个线程
int pthread_cancel(pthread_t th);
//pthread_cansel函数允许一个线程取消th指定的另一个线程
//函数成功,返回0,否则返回非0
/*在另一个进程中终止一个线程代码*/
#include <stdio.h>
#include <stdlib.h>
#include <pthtead.h>
void *func1(void *arg)//线程入口函数
{
printf("pthread start \n");
while(1)
{
printf("pthread living\n");
sleep(1);
}
printf("pthread end \n");
pthread_exit(NULL);
}
void *func2(void *arg)//线程入口函数
{
sleep(5);
pthread_t thr;
thr = *(pthread_t *)arg;
pthread_cancel(thr);
return (NULL);
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
pthread_t thrd1,thrd2;
pthread_created(&thrd1,NULL,func1,NULL);
pthread_created(&thrd2,NULL,func2,&thrd1);
pthread_join(thrd1,NULL);
pthread_join(thrd2,NULL);
printf("main end\n");
return EXIT_SUCCESS;
}
6)判断两个线程是否相等*/
int pthread_equal(pthread_t th1,pthread_t th2);
//pthread_equal函数比较th1和th2是否为同一个线程,由于不可以
将pthread数据类型认为是整数,所以不能用整数的方式去比较
//如果th1与th2相同,函数返回非0值;如果不同,函数返回0
7)线程属性
- 以前调用pthread_created传入的attr参数都是空指针,而不是指向pthread_attr_t结构的指针
- 可以使用pthread_attr_t结构修改线程的默认属性,并把这些属性于创建的线程联系起来
- 可以使用pthread_attr_init函数初始化pthread_attr_t结构
- 调用pthread_attr_init后,pthread_attr_t结构所包含的内容就是操作系统实现支持线程的默认
值.如果要修改其中个别属性的值,需要调用其他函数
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_init(pthread_attr_t *attr);
//函数pthread_attr_init初始化attr结构
//函数pthread_attr_destroy释放attr内存空间
//pthread_attr_t的结构对于应用程序来讲是不透明的,应用程序不需要了解有关结构的内部
//可以通过pthread_attr_t属性在创建线程时就指定线程detach,而不用使用pthread_detach函数
int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate)
//函数pthread_attr_setdetachstate把线程属性设置为下面两个合法值之一
值 说明
PTHREAD_CREATE_DETACHED 设置线程为分离状态
PTHREAD_CREATE_JOINABLE 设置线程为正常状态
/*调整属性示例代码*/
void *func(void *arg)
{
printf("pthread start\n");
while(1)
{
printf("thread is running\n");
sleep(1);
}
pthread_exit(NULL);
}
int main()//控制线程(主线程),main函数退出,所有线程退出
{
pthread_t thrd;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_created(&thrd,&attr,func,NULL);
pthread_attr_destroy(&attr);
sleep(2);
printf("main end\n");
return EXIT_SUCCESS;
}
三 线程同步技术
- 互斥(mutex)是相互排斥的意思,它是一种锁或者信号灯
- 互斥用来保护多个线程共享数据和结构不会被同时修改
一个互斥锁只能有两个状态:locked和unlocked
- 加锁后互斥不让其他线程访问
- 任何时刻只能有一个线程来掌握某个互斥上锁
- 一个线程如果试图在一个已经加锁的互斥上再加锁,这个线程会挂起,
知道加锁的线程释放掉互斥锁为止
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//PTHREAD_MUTEX_INITIALIZER 是初始化一个加速锁的宏定义
//pthread_mutex_lock用于给mutex加锁
//pthread_mutex_unlock用于给mutex解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//检测是否锁
//最恰当的互斥用法
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//初始化了一个MUTEX锁
int count = 0;
void *func1(void *arg)
{
int *a = (int *) arg;
printf("thread%d start\n", *a);
int i;
for (i = 0; i < 10; i++)
{
printf("thread%d is running\n", *a);
sleep(1);
pthread_mutex_lock(&mutex);//给mutex加锁,这是一条原子操作,不可能出现两个线程同时执行这个代码
count++;//这段代码受到保护,永远只有一个线程可以操作
pthread_mutex_unlock(&mutex);//给mutex解锁
}
printf("thread%d end\n", *a);
pthread_exit(NULL);
}
int main(int arg, char * args[])
{
printf("process start\n");
pthread_t thr_d1, thr_d2;
int i[2];
i[0] = 1;
i[1] = 2;
pthread_create(&thr_d1, NULL, func1, &i[0]);
pthread_create(&thr_d2, NULL, func1, &i[1]);
pthread_join(thr_d1, NULL);
pthread_join(thr_d2, NULL);
printf("process end\n");
return 0;
}