UNIXC002 线程的创建和终止、汇合和分离

1. 线程的基本概念

在这里插入图片描述
在这里插入图片描述

  • 进程 = 一个资源 + 多个指令执行序列,这多个指令执行序列指的就是线程。
  • 进程中的所有线程共享映射表,映射表不会变。pc指针指向的是下一条要执行的指令的位置。有多个指令执行序列,所以pc指针在不断改变
  • 右图中,T1、T2是进程中的两个线程,他们共享进程的资源,也有自己的私有资源。
    • 每个线程都有一个线程执行函数,私有资源就在函数对应的栈帧里面。函数又共享进程的所有资源,也就是代码段,数据段,堆里面的内容(没有栈区)。
    • 一个进程的所有线程都共享进程的代码区、数据区、堆区(没有栈区)、环境变量和命令行参数、文件描述符、信号处理函数、当前目录、用户ID和组ID等

1.1 线程的好处

在这里插入图片描述

  • 上图中,左右两个进程里都有各自的三个线程。

  • 进程间要协调工作就必须进行通信,就需要借助内核。而线程间的通信就会高效许多,直接在用户空间就可以实现。提升了效率,节省了空间。

  • 进程间的切换,需要切换进程的PCB(把映像,指令等进行切换),又得借助内核。而线程之间的切换直接在用户空间实现,所有线程共享映射表等资源。

  • 但是多个线程访问一个进程中的资源时,会产生异步访问,造成一些麻烦。

2. 线程的创建

2.1 pthread_create(3)

  • 对上图中 void *(* start_routine)(void *) 解释:
    • 这个位置需要放一个返回值是void*, 形参是void*的函数。类似于 int a;
  • Compile and link with -pthread : 编译和链接的时候要指定pthread

2.2 pthread_selt(2)

在这里插入图片描述

2.3 代码示例

#include "t_stdio.h"
#include <pthread.h>
#include <unistd.h>
// 线程执行的函数
void *doit(void *arg){
    printf("arg: %s\tpid:%d\ttid:%lu\n", (char *)arg, getpid(), pthread_self());
    return NULL;
}

int main(void){
    pthread_t tid;
    // 创建一个新的线程
    pthread_create(&tid, NULL, doit, "new");
    // 到这里进程中有两个线程了,新建线程 和 主线程
    // 并且这两个线程目前是异步的,sleep(1)让主线程先休眠
    // 是为了确保主线程后执行完
    sleep(1);
    // 主线程执行的 ↓
    doit("main");
    return 0;

}
$ gcc thread.c -lpthread
$ ./a.out 
arg: new        pid:5221        tid:140456161720064
arg: main       pid:5221        tid:140456169993984

3. 线程的终止

3.1 return 和 exit(3)

在这里插入图片描述

3.2 pthread_exit(3)

在这里插入图片描述

  • 代码示例看下面的 pthread_join(3)

3.2 pthread_cancel(3)

在这里插入图片描述

  • PTHREAD_CANCELED
  • 代码示例看下面的 pthread_join(3)

4. 线程的汇合和分离

在这里插入图片描述

  • 分离:
    • 如果新建的线程设置了分离的状态,新建线程在终止时会自动把资源释放给系统,主线程不用再去等待回收。
  • 汇合:
    • 如果主线程先到达汇合点,但新建的线程还没有终止,主线程就会阻塞等待回收资源。如果新建线程先达到汇合点,那么主线程来的时候就会立即回收新建线程的资源

4.1 pthread_detach(3)

在这里插入图片描述

4.1.1 代码示例

#include "t_stdio.h"
#include <pthread.h>
#include <unistd.h>

void *doit(void *arg){
    for(int i =0; i<5; i++) {
    printf("thead: %s\tpid: %d\ttid: %lu\n", (char *)arg, getpid(), pthread_self());
    usleep(5000000);
    }
    return NULL;
}
int main(void){
    // 创建新线程
    pthread_t tid;
    pthread_create(&tid, NULL, doit, "new");
    // 分离新线程
    pthread_detach(tid);

    for(int i =0; i<5; i++) {
         printf("pid: %d\ttid: %lu\n", getpid(), pthread_self());
         usleep(10000000);
    }
    return 0;
}
$ ./a.out 
pid: 26507      tid: 139907840493312
thead: new      pid: 26507      tid: 139907832305408
thead: new      pid: 26507      tid: 139907832305408
pid: 26507      tid: 139907840493312
thead: new      pid: 26507      tid: 139907832305408
thead: new      pid: 26507      tid: 139907832305408
pid: 26507      tid: 139907840493312
thead: new      pid: 26507      tid: 139907832305408
pid: 26507      tid: 139907840493312
pid: 26507      tid: 139907840493312

4.2 pthread_join(3)

在这里插入图片描述

  • 注意第二个参数retval是二级指针, 状态码地址会存到*retval

4.2.1 代码示例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *handle(void *arg){
    printf("handle running ...\n");
    sleep(3);
    return (void *)1;
}

void *handle_1(void *arg){
    printf("handle_1 running ...\n");
    sleep(3); 
    // 终止当前线程, 会把(void *)3 传递给调用pthread_join的线程的退出状态码
    // 且会把宏PTHREAD_CANCELED设置为退出状态码
    pthread_exit((void *)3);
}

void *handle_2(void *arg) {
    while(1){
        printf("handle_2 running ...\n");
        sleep(1);
    }
    return NULL;
}

int main(void){
    // 1. retrun 退出
    // retval存放执行void类型存储区的地址
    void *retval;
    pthread_t tid;
    pthread_create(&tid, NULL, handle, NULL);
    // pthread_join 汇合线程,这里是阻塞的(如果新建线程还没有终止,主线程阻塞等待)
    // 线程tid,把退出状态码放到*(&retval),也就是放到retval中。
    // 暂时理解为 被(void*)的1 放到 了 void* 类型的变量retval中。然后又被(int)强制转换以后输出 
    pthread_join(tid, &retval);
    printf("handle exit code ...%d\n",(int )retval);

    // 2. pthread_exit 退出
    pthread_create(&tid, NULL, handle_1, NULL);
    // 线程tid把退出状态码放到retval中
    pthread_join(tid, &retval);
    printf("handle1 exit code ...%d\n",(int )retval);

    // 3. pthread_cancel 退出
    pthread_create(&tid, NULL, handle_2, NULL);
    sleep(3);
    // 3s之后给线程tid发送取消请求
    pthread_cancel(tid);
    // 线程tid把退出状态码放到retval中
    pthread_join(tid, &retval); 
    if(PTHREAD_CANCELED == retval){
        printf("handle2 exit code ...%d\n",(int )retval);
    }
    return 0;
}
$ ./a.out 
handle running ...
handle exit code ...1

handle_1 running ...
handle1 exit code ...3

handle_2 running ...
handle_2 running ...
handle_2 running ...
handle2 exit code ...-1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值