Linux多线程

线程基础

1、为什么要用线程

Linux 创建的初期,内核一直就没有实现“线程”这个东西。每个用户进程有自己的地址空间。
系统为每个用户进程创建一个 task_struct 来描述该进程;该结构体中包含了一个指针指向该进程的虚拟地址空间映射表。
实际上 task_struct 和地址空间映射表一起用来表示一个进程:

由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大;为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,也被称为线程。
在同一个进程中创建的线程共享该进程的地址空间。
Linux 里同样用 task_struct 来描述一个线程。线程和进程都参与统一的调度。

很多个 task_struct 代表一个线程 ( 内核管理线程 ) 紫色内存映射表,通常线程指的是共享相同地址空间的多个任务。
使用多线程的好处:
1、大大提高了任务切换的效率;
2、避免了额外的 TLB & cache 的刷新;
3、多线程编程使用的是一个线程库 pthread, 编译的时候要给 gcc 加上 -lpthread;
4、同样情况下: 多线程的资源消耗要小于多进程, 特别是在一些手持设备中,这个表现的比较明显。
CPU 和内存之间的缓存 (cache) 中有一个部件 TLB (存储当前预取的内存中就近的数据)。当进程切换时候,不同进程的 VMA 完全不同,所有整个 TLB 里面缓冲的数据就失效,要重新刷新 ;当线程切换的时候,由于同一进程中的所有线程都是共享同一 VMA( 虚拟内存入口 ), 就只要部分刷新 TLB。
多线程编程中,要少用慎用全局变量,如果非要使用全局变量或全局结构体变量,则要考虑是否要加资源保护机制,比如:信号量、互斥锁。进程和线程都是解决问题的方式。

线程特点

1、CPU 调度的最小单位;
2、 线程从属于进程;
3、 在同一个进程中创建的线程共享该进程的地址空间,线程和进程统一调度;
4. 线程在程序中以函数的形式体现,一个程序就会产生进程, 产生 4G 空间;线程在栈里面是不同的,线程的空间叫栈帧,每个线程的栈帧是私有的。

多线程编程

NPTL Native POSIX Thread Library 线程库中提供了如下基本操作

创建线程 pthread_create 函数

 案例打印线程的 id

问题:运行时线程代码没有打印输出为什么? 如果子线程后结束。因为 main 所在代码运行结束 return 时会调用 exit()导致进程结束,子线程强制结束了。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <sys/syscall.h> /* For SYS_xxx definitions */

void *fun(void *n)
{
 
    unsigned long sys_tid=syscall(SYS_gettid); //和 ps 看到线程号一致
    unsigned long pthread_tid=pthread_self();

    printf("我是子线程!\n");
    printf("pthread_self():%lu,gettid():%lu\n",pthread_tid,sys_tid);
    sleep(50);
     return (void *)0;
}

int main(void)
{
     pthread_t tid;
     int err;
     if((err=pthread_create(&tid,NULL,fun,NULL))!=0){
         fprintf(stderr, " pthread_create: %s\n",strerror(err));
         exit(1);
     }
 
     printf("this is main\n");
     printf("tid=%lu\n",tid);
     
    sleep(60);//必须加否则子线程不能打印
 
    return 0;
}
在多线程中, pthread_self() 函数获得的线程号是 pthread 库对线程的编号,而不是 Linux 系统对线程的编号。
pthread_create() 返回的线程号,使用 top 命令是查不到的, top 显示的是 Linux 的线程号。
在单线程中, Linux 的线程号和进程号是一样的。
在多线程中,主线程的线程号( main 函数的线程)与进程号一样,其他线程则有各自的线程号。
getpid() 函数不同的是, Linux 并没有直接给一个 gettid() API ,而是使用 syscall() 直接用 SYS_gettid 的系统调用号去获取线程号。
top -H -p 时显示的 tid ,与 syscall(SYS_gettid )返回的一致,与 pthread_self 返回的不一致,后者的与 pthread_create 获得的一致。
查看 pid 创建的所有线程 : ps -T -p pid

多线程等待回收子线程函数

 pthread_exit 函数

 只退出当前子线程,同时给父线程待会退出的状态和值。

pthread_cancel 函数

 参数 thread 是要取消的目标线程的线程 ID。该函数并不阻塞调用线程,它发出取消请求后就返回了。类比于进程的 kill 函数,但是不同的是,调用函数时,并非一定能取消掉该线程,因为这个函数需要线程进到内核时才会被杀掉,所以线程如果一直运行于用户空间,就没有契机进入内核也就无法取消(例 while(1){}空循环),此时可以使用 pthread_testcancel();进入内核。

如果成功, pthread_cancel 返回 0 ,如果不成功, pthread_cancel 返回一个非零的错误码。
https://zhuanlan.zhihu.com/p/175943809 线程生命周期

设置线程的分离属性

int pthread_detach(pthread_t thread);
参数: 设置哪个线程要分离
子线程不需要父线程使用  pthread_join() 清理子线程残留的东西;使用此函数后,会被自动清理, 无需回收。分离之后再使用 pthread_join 进行回收会报错。
没有分离的线程, 它的资源不会释放,需要调用 pthread_join 回收, 进程结束后占用的资源全部释放。
下面是一个示例:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *mypthreadFunction(void *pvoid)
{
    int i = 0;
    while(1)
    {
        printf("thread function, i: %d\n", i++);
        sleep(1);
    }
}
 
int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, mypthreadFunction, NULL);
    pthread_detach(tid); // 将线程分离
    
    //2秒钟之后取消线程
    sleep(2);
    pthread_cancel(tid);
    printf("had cancel the thread!\n");
    
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁静的海2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值