linux应用(三)多线程编程

简述概念与原理

想要编程来让linux系统“同时”执行多个任务,一种方法是使用多进程,也就是我在执行一个程序,哎,我又开一个程序,操作系统你让它俩“同时”运行;这种方式可以,但进程间的切换开销大,通信和同步较为复杂,不合适;我想实现的是,就开一个程序,这个程序的不同代码段可以“同时”运行;这就要用到多线程;

1 啥是线程?

线程是轻量级的进程;
进程是程序的一次动态执行,但是真正执行代码的是线程;操作系统为进程分配资源,进程中的线程来执行任务代码;一个进程至少包含一个线程,即主线程;其他线程(代码创建出来的)为子线程;
注:线程库不在linux内核中,使用线程需要在编译时链接;

进程与线程:

  • 每个用户进程有自己的地址空间,系统为每个用户进程创建一个task_struct来描述该进程,该结构体中包含了一个指针指向该进程的虚拟地址空间映射表;
    实际上task_struct 和地址空间映射表一起用来表示一个进程
  • 由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大,
  • 在同一个进程中创建的线程共享该进程的地址空间
  • Linux里同样用task_struct来描述一个线程。线程和进程都参与统一的调度
  • 线程是系统调度的最小单位

通常线程指的是共享相同地址空间的多个任务
使用多线程的好处: 大大提高了任务切换的效率;避免了额外的TLB & cache的刷新;

进程:
在这里插入图片描述
线程:
在这里插入图片描述

2 线程私有的资源

线程在进程中,那么多个线程就会共享这个进程的资源,同时,线程有自己的私有资源;*可以这样辅助理解:线程看上去就像一个子函数一样,所以私有栈和局部变量,有自己的ID等,而且子函数可以访问全局的所有东西; *
线程私有:

  • 线程ID即TID;
  • PC(程序计数器)和相关寄存器
  • 堆栈:局部变量和返回地址
  • 错误号 (errno)
  • 信号掩码和优先级
  • 执行状态和属性

线程共享:

  • 可执行的指令,或者说程序的代码;
  • 地址空间
  • 静态数据(全局和静态)
  • 进程中打开的文件描述符
  • 当前工作目录
  • 用户ID
  • 用户组ID

一 线程的创建

1 线程句柄 / 线程描述符

要创建线程,首先先要创建一个句柄,线程的句柄或者说linux下的描述符,是pthread_t类型的变量;
(句柄这个词,翻译得简直糟糕透顶,初学者迷惑利器,但是理解了之后反而好用的很,但也不是翻译的功劳,而是这个词代表的本来含义很好用,再说一次,翻译得糟糕透顶)

pthread_t myThread1;

2 创建线程使用库函数pthread_create()

 #include  <pthread.h>
int pthread_create(pthread_t *thread,
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *),
                   void *arg);

2.1 四个形参分别为:
(1) 线程句柄;
(2) 一个结构体指针,用于设置线程的属性,一般使用默认属性,传入NULL即可;(属性将在后面专门描述)
(3) 一个函数指针,用于把线程对应的子函数传入;(线程执行传入的子函数);
(4) 一个void指针,用来给线程传参,注意第三个形参的函数形参就是void*,就是往那里传,不传参写NULL;
2.2 返回值
如果成功创建线程,pthread_create() 函数返回数字 0,反之返回非零值。各个非零值都对应着不同的宏,指明创建失败的原因,常见的宏有以下几种:(定义在errno.h文件中)
EAGAIN:系统资源不足,无法提供创建线程所需的资源。
EINVAL:传递给 pthread_create() 函数的 attr 参数无效。
EPERM:传递给 pthread_create() 函数的 attr 参数中,某些属性的设置为非法操作,程序没有相关的设置权限。
2.3 常用的两种形式
(1) 一般来说,我们不需要改线程属性,也不需要给线程传参,那么创建很简单:pthread_create(句柄指针, NULL, 子函数名, NULL);
(2) 如果需要传参:pthread_create(句柄指针, NULL, 子函数名, (void*)参数指针);
看段代码:
为啥要加sleep(),为了让主线程停下来,好让子线程执行,否则,主线程可不管你子线程,主线程很快执行完直接就结束整个进程,所有线程都会被强行终止,直接没有现象;

  1 #include <stdio.h>
  2 #include <unistd.h>   //调用 sleep() 函数
  3 #include <pthread.h>  //调用 pthread_create() 函数
  4
  5 void *ThreadFun(void *arg)//一个普通子函数,但是格式void*(函数名)(void*),可以往pthread_create()里传;
  6 {
   
  7     if (arg == NULL) {
   
  8         printf("arg is NULL\n");
  9     }
 10     else {
   
 11         printf("%s\n", (char*)arg);//对(void *)不能直接解引用,要转换成其他类型指针;
 12     }
 13     return NULL;
 14 }
 15
 16 int main()
 17 {
   
 18     int res;
 19     char * str = "hello";
 20     //定义两个表示线程的变量(标识符)
 21     pthread_t myThread1,myThread2;
 22     //创建 myThread1 线程
 23     res = pthread_create(&myThread1, NULL, ThreadFun, NULL);
 24     if (res != 0)
 25     {
   
 26         printf("线程创建失败");
 27         return 0;
 28     }
 29     sleep(5);  //令主线程等到 myThread1 线程执行完成
 30
 31     //创建 myThread2 线程
 32     res = pthread_create(&myThread2, NULL, ThreadFun,(void*)str);
 33     if (res != 0)
 34     {
   
 35         printf("线程创建失败");
 36         return 0;
 37     }
 38     sleep(5); // 令主线程等到 mythread2 线程执行完成
 39     return 0;
 40 }
bai@c:~/test$ gcc test.c -lpthread
bai@c:~/test$ ./a.out
arg is NULL				//打印完后会等待5s;
hello					//打印完后等待5s
bai@c:~/test$

很有用的函数:

#include <pthread.h>
pthread_t pthread_self(void);

可以返回当前线程的ID号,可以在线程中执行打印等操作;

二 线程的终止

1 终止方法

多线程程序中,终止线程执行的方式有 3 种,分别是:

  • 线程执行完成后,自行终止;
  • 线程执行过程中遇到了 pthread_exit() 或者 return,也会终止执行;
  • 线程执行过程中,接收到其它线程发送的“终止执行”的信号,然后终止执行。
1.1 线程内终止:pthread_exit()和return

1.1.1 pthread_exit()函数和return都能终止函数和返回指定值;区别是return用于结束任意一个函数,pthread_exit()用于终止一个线程,在main()里非线程子函数的任意地方,包括调用的普通子函数内只要出现pthread_exit()就会终止主线程;导致后续代码无法执行;

#include  <pthread.h>
 void  pthread_exit(void *retval);

pthread_exit()本身不返回值,但可以用一个指针向其他线程传递值;

  1 #include <stdio.h>
  3 #include <pthread.h>  
 15 void test()
 16 {
   
 17     printf("test\n");
 18     pthread_exit(NULL);
 19 }
 20 int main()
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值