【Linux】多线程(2)

简介: 【Linux】多线程

6、线程分离

  • 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
  • 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。

线程分离的函数:

int pthread_detach(pthread_t thread);

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

下面我们进行线程分离以后再进行等待,看看会发生什么。

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
void* threadRoutine(void* args)
{
    int cnt = 3;
    while (cnt--)
    {
        std::cout << "new thread run..." << std::endl;
    }
    return nullptr;
}
int main()
{
    pthread_t t1;
    pthread_create(&t1, nullptr, threadRoutine, nullptr);
  // 线程分离
    pthread_detach(t1);
  //  线程等待
    int error = pthread_join(t1, nullptr);
    if (error == 0)
    {
        std::cout << "等待成功" << std::endl;
    }
    else
    {
        std::cout << "等待失败,错误码" << error << ",错误原因:" << strerror(error) << std::endl;
    }
    return 0;
}

由于我们对新线程进行了线程分离,所以主线程等待错误,将错误信息进行打印,主线程退出,新线程也要跟着退出。

三、Linux线程数据

1、共享与私有的对比

我们知道线程共享进程数据,但也拥有自己的一部分私有数据

  • 线程ID。
  • 一组寄存器。(存储每个线程的上下文信息)
  • 。(每个线程都有临时的数据,需要压栈出栈)
  • errno。(C语言提供的全局变量,每个线程都有自己的)
  • 信号屏蔽字。
  • 调度优先级。

进程的多个线程共享 同一地址空间,因此代码段、数据段都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表。(进程打开一个文件后,其他线程也能够看到)
  • 每种信号的处理方式。(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
  • 当前工作目录。(pwd)
  • 用户ID和组ID。

2、Linux线程库的深入理解

我们知道Linux线程库是一个动态库,当我们多线程程序在运行时动态库需要被加载到共享区内的。

创建出来的线程是要被进行管理的,由于Linux不提供真正的线程,只提供LWP,也就意味着操作系统只需要对内核执行流LWP进行管理,而供用户使用的线程接口等其他数据,应该由线程库自己来管理,因此管理线程的工作就应该在线程库里进行。

于是线程库里面为我们提供了一个个类似于tid的结构,这个结构里面包含了struct_pthread(存储了线程的许多属性),线程局部存储线程栈(这就是为什么每个线程都有独立的栈结构,主线程用的是进程系统栈,新线程用的是库中提供的栈),我们以前常用的pthread_t类型存储的值就是这个类似于tid结构的首地址

下面一个代码将pthread_t转换为16进制的地址:

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <string>
#include <pthread.h>
// 16进制转换
std::string HexAdress(pthread_t tid)
{
    char str[64];
    snprintf(str, sizeof(str), "0x%x", tid);
    std::string s (str);
    return s;
}
void* threadRoutine(void* args)
{
    int cnt = 3;
    while (cnt--)
    {
        std::cout << "new thread aderss : " << HexAdress(pthread_self()) << std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t t1;
    pthread_create(&t1, nullptr, threadRoutine, nullptr);
  // 线程分离
    pthread_detach(t1);
    sleep(5);
    return 0;
}


实际上我们线程创建对应的pthread_creat使用的就是clone系统调用,从前到后参数的意义以此是:要执行的函数,独立栈的地址,创建的一些选项,传给参数1的函数参数。


__threadGCC内置的线程局部存储设施,__thread修饰的全局变量(内置类型)每一个线程有一份独立实体,各个线程的值互不干扰,因为它们都被存储到了线程库管理结构中的线程局部存储内。

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <pthread.h>
// __thread修饰全局变量
__thread int g_val = 10;
void* threadRoutine(void* args)
{
    long long a = (long long)args;
    int cnt = 3;
    while (cnt--)
    {
        std::cout << "线程" << a << ",g_val : " << (g_val++ * a) << std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t t1, t2, t3;
    pthread_create(&t1, nullptr, threadRoutine, (void*)1);
    usleep(1000);
    pthread_create(&t2, nullptr, threadRoutine, (void*)2);
    usleep(1000);
    pthread_create(&t3, nullptr, threadRoutine, (void*)3);
  // 线程分离
    pthread_detach(t1);
    pthread_detach(t2);
    pthread_detach(t3);
    sleep(5);
    return 0;
}

相关文章
|
4月前
|
消息中间件 存储 缓存
【嵌入式软件工程师面经】Linux系统编程(线程进程)
【嵌入式软件工程师面经】Linux系统编程(线程进程)
101 1
|
2月前
|
算法 Unix Linux
linux线程调度策略
linux线程调度策略
55 0
|
2月前
|
存储 设计模式 NoSQL
Linux线程详解
Linux线程详解
|
2月前
|
缓存 Linux C语言
Linux线程是如何创建的
【8月更文挑战第5天】线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。
|
2月前
|
负载均衡 Linux 调度
在Linux中,进程和线程有何作用?
在Linux中,进程和线程有何作用?
|
2月前
|
缓存 Linux C语言
Linux中线程是如何创建的
【8月更文挑战第15天】线程并非纯内核机制,由内核态与用户态共同实现。
|
4月前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
4月前
|
Linux API
Linux线程总结---线程的创建、退出、取消、回收、分离属性
Linux线程总结---线程的创建、退出、取消、回收、分离属性
|
4月前
|
API
Linux---线程读写锁详解及代码实现
Linux---线程读写锁详解及代码实现
|
3月前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
43 0
下一篇
无影云桌面