Linux||线程函数控制函数||单/多线程

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在各个线程之间切换等也需要花费大量的时间
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值