【项目 线程 1】 3.1线程概述 3.2创建线程 3.3终止线程 3.4连接已终止的线程


3.1线程概述

线程概述

在这里插入图片描述并发:两队人用同一个咖啡机(本质上同一时刻只有一个进程在运行,但是切换非常快,所以看起来是好几个进程同时进行)
并行:两队人用两个咖啡机

查看指定进程的线程:ps -Lf pid(进程号)
在这里插入图片描述

线程和进程区别

在这里插入图片描述

线程和进程虚拟地址空间

进程是读时共享,写时复制。但不论怎么说,只要创建了一个新进程,内核一定先被复制。写的时候还要创建新的实际物理空间。

线程共享虚拟地址空间,只是**.text段(代码段)以及栈空间**有不一样。.text会分成一个个小段,线程1、线程2…分开存储在不同的段.text。各线程执行各自的代码。栈空间也会有不同,分成一个个小段分给各线程。
在这里插入图片描述

线程之间共享和非共享资源

在这里插入图片描述信号掩码:每个线程的信号阻塞不一样

NPTL

Linux系统线程库的发展概述
在这里插入图片描述查看当前线程库版本:
在这里插入图片描述

3.2 创建线程

线程操作

在这里插入图片描述

创建线程

在这里插入图片描述

出现报错及原因

在这里插入图片描述

线程是第三方库不是标准系统库,需要通过-l指定去指定库的名称
解决方案如下:加上第三方库 -pthread 或者 -lpthread

wcx@wcx-virtual-machine:~/linux/lesson29$ gcc pthread_create.c -o pthread_create -pthread

在这里插入图片描述

main函数中所有执行的代码为主线程的代码,子线程执行的代码为回调函数的代码

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

//函数指针,void*为万能指针
void * callback(void * arg) {
    printf("child thread...\n");
    printf("arg value: %d\n", *(int *)arg);
    return NULL;
}

int main() {

    pthread_t tid;

    int num = 10;

    // 创建一个子线程
    int ret = pthread_create(&tid, NULL, callback, (void *)&num);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    } 

    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    //预防子线程还在创建或子线程未抢占执行权,程序就退出,预防子线程的未被执行
    sleep(1);

    return 0;   // exit(0);
}

显示结果:
在这里插入图片描述

3.3终止线程

让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

	//该句没有被执行
    printf("main thread exit\n");

	//没有执行
    return 0;   // exit(0);

在这里插入图片描述

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

void * callback(void * arg) {
    printf("child thread id : %ld\n", pthread_self());
    return NULL;    //相当于 pthread_exit(NULL);终止子线程
} 

int main() {

    // 创建一个子线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, callback, NULL);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    // 主线程
    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());

    // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。
    pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程

	//该句没有被执行
    printf("main thread exit\n");

	//没有执行
    return 0;   // exit(0);
}

在这里插入图片描述
下图说明主线程和子线程是交替执行的
在这里插入图片描述

3.4连接已终止的线程

在这里插入图片描述子进程一定是被父进程回收,子线程不一定需要被父线程回收,可以被任意线程回收,但一般是主线程回收子线程。如果子线程结束后不被回收,也会产生僵尸线程。

为何使用二级指针?
要想改变一级指针的值,函数调用传入参数为二级指针。
因为返回值是一个指针,想通过retval传出返回值,肯定要用指针。

int a = 0
void act(&a)(){}

肯定是这样才能改变a的值。
同理如果a是一个指针类型,形参理所当然就是二级指针

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

//全局变量,若为局部变量,栈空间数据,最后获取的是个随机的值
//线程退出时记得一定返回的是全局变量的值
int value = 10;

void * callback(void * arg) {
    printf("child thread id : %ld\n", pthread_self());
    // sleep(3);
    // return NULL; 
    // int value = 10; // 局部变量
    pthread_exit((void *)&value);   // return (void *)&value;
} 

int main() {

    // 创建一个子线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, callback, NULL);

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    // 主线程
    for(int i = 0; i < 5; i++) {
        printf("%d\n", i);
    }

    printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());

    // 主线程调用pthread_join()回收子线程的资源
    int * thread_retval;//定义一个一级指针
    //传递一级指针的地址作为二级指针,并转换为void **
    ret = pthread_join(tid, (void **)&thread_retval);
    

    if(ret != 0) {
        char * errstr = strerror(ret);
        printf("error : %s\n", errstr);
    }

    printf("exit data : %d\n", *thread_retval);

    printf("回收子线程资源成功!\n");

    // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。
    pthread_exit(NULL);

    return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值