认识Linux线程

创建多个进程(父子进程)有什么缺点?

虽然遵循 “读时共享 写时拷贝” 但仍然存在资源的消耗与浪费

什么是线程?

进程是操作系统分配资源的最小单位

线程是操作系统调度的最小单位

线程是操作系统执行的单位

类比于足球比赛 进程是教练 线程是场上的队员

进程是资源管家 线程去干活

进程有独立的地址空间 而线程没有

线程有权访问所属进程拥有的资源

实际上

无论是创建进程fork()

还是创建线程pthread_create()

底层实现都是调用同一个 内核函数 clone()

如果是复制原先的地址空间 就创建了进程 类似于深拷贝

如果是共享原先的地址空间 就创建了线程 类似于浅拷贝 (栈空间是不共享的)

线程是轻量级的进程 在Linux下线程的本质就是进程

Linux底层/内核不区分进程或者线程 两者只是在应用层/上层进行区分

线程的优缺点

优点

1.提高程序的并发性

2.开销比进程小

3.方便数据的通信与共享

缺点

1.库函数不稳定(使用第三方库创建的线程)

2.编程复杂 调试困难

3.对信号的支持较差

线程常用操作

1.引入头文件 #include<pthread.h>

2.链接线程库 gcc XXX.c -pthread

线程号获取

pthread_t pthread_self(void);

线程号只在所属的进程中唯一

 一个进程至少包含一个线程 默认有一个线程

返回线程ID 通常用 pthread_t (无符号长整形%lu) 数据类型表示

比较线程ID

int pthread_equal(pthread_t t1,pthread_t t2); 

比较两个线程ID是否相同 

有时线程ID也会用结构体表示

所以不能直接用运算符比较t1 t2

应当用函数比较 关注代码的可移植性

创建线程

int pthread_create(...);

创建线程 成功则返回0

失败时不可以使用perror()查看错误

int pthread_create(pthread_t *thread,                     //传出线程标识符地址

                                const pthread_attr_t * attr,       //线程属性结构体地址 默认设置为NULL

                                void *(*start_routine)(void *),   //回调函数 线程启动后执行的任务

                                void *arg);                                //传给回调函数的参数(上一行的参数)

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

void *fun(void *arg)
{
    printf("new thread tid: %lu\n", pthread_self());
    printf("get number %d\n", *(int*)arg);

    return NULL;
}

int main(void)
{
    int ret = -1;
    pthread_t tid = -1;
    int number = 520;
    
    ret = pthread_create(&tid, NULL, fun, &number);
    if(0 != ret)
    {
        printf("failed.\n");
        return 1;
    }

    printf("success.\n");
    printf("main thread tid: %lu\n", pthread_self());

    getchar();

    return 0;
}

注意:

当 主线程(main thread) return 0 时 认为进程已经结束

因此 创建的新线程(new thread)也会被立即终止

为了确保子线程执行完毕 需要阻塞主线程的退出

当一个进程创建了多个线程的时候

多个线程是交替且同时运行的 运行顺序跟CPU调度相关 无法预测

运行效果:

线程的资源共享与回收

线程共享进程的数据段(全局变量)和堆区数据

线程不共享栈空间 每个线程有一个独立的栈空间

进程使用wait()和waitpid()进行回收资源

线程使用

int pthread_join(pthread_t thread, void **retval);

此函数会阻塞至线程结束 并回收线程资源

如果线程结束后不被回收 则会变成僵尸线程

其中 thread 是被等待的线程ID

retval 传出线程回调函数返回的地址

成功回收 函数返回0

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

void *fun(void *arg)
{
    printf("new thread tid: %lu\n", pthread_self());

    return (void *)0x520;
}

int main(void)
{
    int ret = -1;
    pthread_t tid = -1;
    void *retp = NULL;
    
    ret = pthread_create(&tid, NULL, fun, NULL);
    if(0 != ret)
    {
        printf("failed.\n");
        return 1;
    }

    printf("main thread running...\n");    

    ret = pthread_join(tid, &retp);
    if(0 != ret)
    {
        printf("failed.\n");
        return 1;
    }

    printf("retp: %p\n", retp);
    printf("main thread exit.\n");

    return 0;
}

运行效果:

线程的分离、退出、取消

分离 int pthread_detach(pthread_t thread);

可以把线程设定为

线程终止后立即由内核回收资源 不保留终止状态

这样就不必(也不能)调用pthread_join()进行资源回收

成功约定 返回0

退出 void pthread_exit(void *retval);

在线程的回调函数中写 pthread_exit(NULL); 实现线程退出

不能在线程中调用exit(0)退出线程 否则整个进程将被终止

取消 int pthread_cancel(pthread_t thread);

一个线程可以杀死同进程中的另一个线程

不稳定 有延时(不是立即杀死)需要等待一个取消点(如系统调用)

成功返回0

线程的属性

创建线程时调用的int pthread_create(...);

其中第二个参数是一个结构体

1.线程的分离状态

2.线程栈的大小

3.线程栈的警戒缓冲区大小

4.线程栈的最低地址

属性值必须通过相关函数操作

pthread_attr_init();

必须在创建线程之前调用

int ret = -1;
pthread_attr_t attr;
ret = pthread_attr_init(&attr);
//设置相关状态
//创建对应线程
ret = pthread_attr_destroy(&attr);

线程的注意点总结

1.如果希望主线程退出其他线程不退出

        主线程中调用 pthread_exit() 不能 return 0

2.避免僵尸线程

        调用 pthread_join() 回收

                被join的线程不应当返回栈空间的值

                可能在函数返回前 join函数已经将栈空间释放

        或者 设置线程分离 调用 pthread_detach()

        或者 创建线程前 初始化并设置好线程属性

3.避免使用信号 否则使用多进程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

没伞的男孩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值