文章目录
Linux线程控制
POSIX线程库
- 与线程相关的函数构成了一个完整的系列,绝大多数函数的名字都是以==pthread_==打头的
- 要使用这些函数库,要通过引入头文件<pthread.h>
- 链接这些线程函数库时要使用编译器命令的"-lpthread"选项
一:线程控制的相关函数
1.【创建线程】
pthread_create的使用
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
void * ThreadEntry(void *arg){
(void) arg;
while(1){
printf("In ThreadEntry %lu\n",
pthread_self());
//pthread_self可以查看线程本身
sleep(1);
}
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,NULL);
while(1){
printf("In mian Thread%lu\n",
pthread_self());
sleep(1);
}
return 0;
}
【pthread_create函数简介】
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
//thread:返回线程ID
//attr:设置线程的属性,attr为NULL表示使用默认属性
//start_routine:是个函数地址,线程启动后要执行的函数
//arg:传给线程启动函数的参数
//返回值:成功返回0;失败返回错误码
线程ID及进程地址空间布局
- pthread_create函数会产生一个线程ID,存放在第一个参数指向的地址中.
- pthread_create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴.线程库的后续操作,就是根据该线程ID来操作线程的
- 线程库NPTL提供了pthread_self函数,可以获取线程自身的ID
pthread_t pthread_self(void);
- pthread_t 类型的线程ID,本质是一个进程地址空间上的一个地址
- 线程ID
ldd [可执行程序名]
查看它依赖那些库
【查看线程信息】
1.ps -eLf | grep [进程名] |
---|
可以打印出轻量级的进程(线程)
查看所有的线程信息(LWP这一列表示线程)
ps得到的线程id是站在内核角度给PCB加了一个编号
pthread_self 得到的是线程id 是站在posix线程库的角度
- 2.
pstack [pid]
打印出线程的信息和调用栈,看到的线程pid是16进制的和程序运行的结果是匹配的,LWP是内核角度看到的pid
- 3.gdb调式
gdb attach [pid]
让gdb 调试捕捉到该继承
info thead
查看该进程的线程信息
thread n
切换到n号线程
bt
bt查看指定线程的调用栈
2.线程终止
- 只需要终止某个线程,而不终止整个线程的方法
- 1.从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 2.线程可以调用pthread_ exit终止自己。
- 3.一个线程可以调用pthread_ cancel终止同一进程中的另一个线程
pthread_exit的使用
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
void * ThreadEntry(void *arg){
(void) arg;
int count = 5;
while(count--){
printf("In ThreadEntry %lu\n",
pthread_self());
sleep(1);
}
pthread_exit(NULL);
return NULL;
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,NULL);
while(1){
printf("In mian Thread=%lu\n",
pthread_self());
sleep(1);
}
return 0;
}
- pthread_exit 函数简介
功能:线程终止
原型: void pthread_exit(void *value_ptr);
参数: value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样线程结束的时候无法返回到它的调用者(自身)
pthread_cancel的使用
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
void * ThreadEntry(void *arg){
(void) arg;
while(1){
printf("In ThreadEntry %lu\n",
pthread_self());
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,NULL);
while(1){
printf("In mian Thread=%lu\n",
pthread_self());
pthread_cancel(tid);
sleep(1);
}
return 0;
}
- pthread_cancle 函数简介
1.让线程入口函数执行结束(最主要使用的结束方式)
2.pthread_exit 让本线程结束,pthread_exit参数是一个void* 表示线程结束的返回结果(很少用到)
3.pthread_cancel 让任意一个线程结束(本进程中的线程)不太推荐使用, pthread_cancel执行后对应的线程不一定会立刻结束
- 功能:
- 取消一个执行中的线程
- 原型:
int pthread_cancel(pthread_t thread);
- 参数
- thread:线程ID
- 返回值:
- 成功返回0;失败返回错误码
事务
要求具有 原子性 要么就不做,要么就做完。千万别做一半就半途而废,如果半途而废可能会导致数据被破坏。带来的后果非常严重
3.等待线程
pthread_join的使用
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
void * ThreadEntry(void *arg){
(void) arg;
while(1){
printf("In ThreadEntry\n");
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,NULL);
printf("In mian Thread\n");
pthread_join(tid,NULL);
printf("new thread 已经被回收了!\n");
return 0;
}
【函数简介】
- 功能:
- 等待线程结束
- 原型
int pthread_join(pthread_t thread, void **value_ptr);
- 参数
- thread:线程ID
- value_ptr:它指向一个指针,后者指向线程的返回值
- 返回值:
- 成功返回0;失败返回错误码
(目的和进程等待类似,防止出现类似僵尸进程的内存泄露的情况)
pthread_join : 其实是为了等待对应线程结束然后再继续执行代码
4.线程分离(类似于 忽略SIGCHLD信号)
pthread_detach的使用
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
void * ThreadEntry(void *arg){
(void) arg;
while(1){
printf("In ThreadEntry \n");
sleep(1);
}
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEntry,NULL);
pthread_detach(tid);
//分离刚才创建的新线程
while(1){
printf("In main Thread\n");
sleep(1);
}
return 0;
}
【函数简介】
pthread_join : 其实是为了等待对应线程结束然后再继续执行代码.
线程回收后,就不需要使用 pthread_join 来显示回收了
pthread_detach:可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分
例如:计算一个很多大的矩阵相乘,可以使用多线程的方式来计算,每个线程计算其中的一部分结果。最后所有的线程执行完成之后由主线程汇总最终结果 ,主线程就需要pthread_join 来保证所有的线程都算完了,主线程才继续执行
二:多线程利用多核资源
- 占满一个CPU
#include<stdio.h>
#include<unistd.h>
void ThreadEnter(void* arg){
(void) arg;
while(1);
return NULL;
}
int main(){
pthread_t tid;
pthread_create(&tid,NULL,ThreadEnter);
return 0;
}
- 占满两个CPU
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
//多线程利用多核资源
//该程序负责占用两个CPU
void *ThreadEntry(void *arg){
(void) arg;
while(1);
return NULL;
}
int main(){
pthread_t tid;
// pthread_t tid,tid1,tid2;
pthread_create(&tid,NULL,ThreadEntry,NULL);
// pthread_create(&tid1,NULL,ThreadEntry,NULL);
// pthread_create(&tid2,NULL,ThreadEntry,NULL);
while(1);
return 0;
//CPU 的功率是有限度的
//当线程数目达到CPU的上线的时候,继续增加线程数目,会
//降低CPU的效率,线程争夺CPU资源,CPU花费更多的时间在
//线程之间切换
}
线程不是越多越好,达到一定程度,cpu已经全部达到上限,此时线程继续增加,非但不能提高效率,反而降低效率(线程多了,调度的开销也就更大,效率就会受影响)
cpu密集/Io密集
到底该搞几个线程呢? 线程数目和工作的任务类型有关,究竟多少合适。要通过测试的方式来判定
使用时尽量让所有的cpu和IO等都使用起来。
在节省资源时要尽量多的节省,使用的时候要都能使用的起来。
通过多线程提到程序执行效率
三:使用单/多线程验证线程执行效率
【题目】
统计单线程或者多线程求一个较大数组中各个数字的平方的计算
单线程版本
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/time.h>
#include<stdlib.h>
#include<stdint.h>
#define SIZE 1000000
//时间戳查看 :data +%s
//us ->微秒
//ms ->毫秒
int64_t GetUs(){
//时间戳可能为负(闰秒),返回值类型需为有符号型
struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_sec*1000000+tv.tv_usec;
//当前的秒数*1000000转化为微秒+当前微秒数
}
void Calc(int *arr,int beg,int end){
for(int i=beg;i<end;++i){
arr[i]=arr[i]*arr[i];
}
}
int main()
{
//记录开始时间
int64_t beg=GetUs();
int *arr=(int *)malloc(sizeof(int)*SIZE);
Calc(arr,0,SIZE);
//记录结束时间
//两者的差为执行的时间
int64_t end=GetUs();
printf("执行时间=%ld \n",end-beg);
return 0;
}
多线程版本
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/time.h>
#include<stdlib.h>
#include<stdint.h>
#define SIZE 100000000
#define THREAD_NUM 2
typedef struct Arg{
int beg;
int end;
int *arr;
}Arg;
int64_t GetUs(){
struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_sec*1000000+tv.tv_usec;
}
void Calc(int *arr,int beg,int end){
for(int i=beg;i<end;++i){
arr[i]=arr[i]*arr[i];
}
}
void * ThreadEntry(void* arg){
Arg* p= (Arg*)arg;
Calc(p->arr,p->beg,p->end);
return NULL;
}
//线程1 Calc(arr,0,SIZE/2)
//线程2 Calc(arr,SIZE/32, SIZE)
int main()
{
int *arr=(int *)malloc(sizeof(int)*SIZE);
Arg args[THREAD_NUM];
int base=0;
for(int i=0;i<THREAD_NUM;++i){
args[i].beg=base;
args[i].end=base+ SIZE/THREAD_NUM;
args[i].arr=arr;
base+=SIZE/THREAD_NUM;
}
int64_t beg=GetUs();
pthread_t tid[THREAD_NUM];
for(int i=0;i<THREAD_NUM;++i){
pthread_create(&tid[i],NULL,ThreadEntry,&args[i]);
}
for(int i=0;i<THREAD_NUM;++i){
pthread_join(tid[i],NULL);
}
int64_t end=GetUs();
printf("执行时间=%ld\n",end-beg);
return 0;
}
【总结】
- 单线程执行效率比较低下
- 多线程执行效率较单线程而言比较高,但是过多线层参与该计算,对计算效率没有明显的提高,反而会降低计算效率.因为CPU在各个线程之间切换等也需要花费大量的时间