【Linux】线程库

一、线程库管理

tid其实是一个地址

复制代码
void* start(void* args)
{
    const char* name = (const char *)args;
    while(true)
    {
        printf("我是新线程 %s ,我的地址:0x%lx\n",name,pthread_self());
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t tid1;
    pthread_create(&tid1,nullptr,start,(void*)"thread-1");
    printf("我是主线程,我的地址:0x%lx\n",pthread_self());
    void* ret = nullptr;
    pthread_join(tid1,&ret);
    return 0;
}

zxw@hcss-ecs-cc58:~/linux_-ubuntu/class/lesson7$ ./mythread 
我是主线程,我的地址:0x7f9dbdc333c0
我是新线程 thread-1 ,我的地址:0x7f9dbdc2f640
我是新线程 thread-1 ,我的地址:0x7f9dbdc2f640
我是新线程 thread-1 ,我的地址:0x7f9dbdc2f640

首先,我们知道pthread. h不是操作系统的接口,而是原生线程库 。那么用户创建的线程,操作系统无法管理,则需要线程库来进行管理。他从系统中获取轻量级进程相关属性,从用户中也获取一些属性,这样就先描述起来了,再通过数据结构将线程组织起来,就将线程管理好了。

那么线程库如何管理呢,在哪管理呢?

在进程地址空间中,通过 mmap (共享区)加载了动态库,我们使用的 pthread 库就在该区域。pthread 库会管理进程中的每一个线程,它使用一系列的数据结构(如线程控制块)来组织和维护线程的相关信息。

每个线程还拥有独立的栈空间,主线程的栈由操作系统在进程启动时自动创建,位于进程地址空间的默认栈区;而通过 pthread_create 创建的其他线程,其栈空间默认由操作系统在进程地址空间的线程私有区域进行分配。

当调用 pthread 相关函数时,实际上是对 pthread 库所管理的这些数据结构进行访问和操作,从而实现对线程的创建、同步、销毁等管理功能。

那么现在,我们也可以理解 pthread_t tid 是什么了,他不就是每一个线程在进程地址空间的起始地址嘛,我们pthread_create 对tid进行写入,因为需要创建对应的数据结构,找到起始地址,然后返回,后续用户要继续对线程进行控制,等待啊,终止啊,分离啊,取消啊。都需要传入tid,也就是能找到在进程地址空间的位置后,才可以处理。

二、线程的局部存储

复制代码
int g_val = 100;
 
void *TreadFun(void *arg)
{
    while (1)
    {
        cout << "我是一个新线程,g_val: " << g_val << ",&g_val: " << &g_val << endl;
        g_val++;
        sleep(1);
    }
    return nullptr;
}
 
int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, TreadFun, (void *)"Thread 1");
    while (1)
    {
        cout << "我是一个主线程,g_val: " << g_val << ",&g_val: " << &g_val << endl;
        sleep(1);
    }
    pthread_join(tid,nullptr);
}

zxw@hcss-ecs-cc58:~/linux_-ubuntu/class/lesson7/Pthread$ ./testThread 
我是一个主线程,g_val: 100,&g_val: 0x55a1a3616014
我是一个新线程,g_val: 100,&g_val: 0x55a1a3616014
我是一个主线程,g_val: 101,&g_val: 0x55a1a3616014
我是一个新线程,g_val: 101,&g_val: 0x55a1a3616014
我是一个主线程,g_val: 102,&g_val: 0x55a1a3616014
我是一个新线程,g_val: 102,&g_val: 0x55a1a3616014
我是一个主线程,g_val: 103,&g_val: 0x55a1a3616014
我是一个新线程,g_val: 103,&g_val: 0x55a1a3616014

如果我们给全局变量前添加上__thread,GCC/G++编译器提供的一个扩展,用于声明线程局部存储变量。

复制代码
__thread int g_val = 100;

重新执行程序,发现地址发生改变

复制代码
我是一个主线程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一个新线程,g_val: 102,&g_val: 0x7f8c2a39763c
我是一个主线程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一个新线程,g_val: 103,&g_val: 0x7f8c2a39763c
我是一个主线程,g_val: 100,&g_val: 0x7f8c2a39b3bc
我是一个新线程,g_val: 104,&g_val: 0x7f8c2a39763c

因为我们添加的__thread 会在G++编译时,给每个线程的局部存储空间里将变量拷贝进程,私有一份,于是每个线程自己管理自己的那一份资源。

__thread只能修饰内置类型,如string这种数据结构和自定义类型无法处理。

三、线程封装

复制代码
#ifndef _THREAD_HPP__
#define _THREAD_HPP__
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdio>
using namespace std;

namespace MyThread
{
    template <typename T>
    using func_t = function<void(T)>;

    static int number = 1;
    enum TSTATUS
    {
        NEW,
        RUNNING,
        STOP
    };
    template <typename T>
    class Thread
    {
    private:
        // 成员方法! void* Routinue(Thread* this,void* args)
        static void *Routinue(void *args)
        {
            Thread<T> *t = (Thread<T> *)args;
            t->_status = TSTATUS::RUNNING;
            t->_func(t->_data);
            return nullptr;
        }

        void EnableDetach()
        {
            _joinable = false;
        }

    public:
        Thread(func_t<T> func, T data) : _func(func), _status(TSTATUS::NEW), _joinable(true), _data(data)
        {
            _name = "Thread-" + to_string(number++);
            _pid = getpid();
        }

        bool Start()
        {
            if (_status != TSTATUS::RUNNING)
            {
                int n = ::pthread_create(&_tid, nullptr, Routinue, this);
                if (n != 0)
                    return false;
                return true;
            }
            return false;
        }

        bool Stop()
        {
            if (_status == TSTATUS::RUNNING)
            {
                int n = ::pthread_cancel(_tid);
                if (n != 0)
                    return false;
                return true;
            }
            return false;
        }

        bool Join()
        {
            if (_joinable)
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n != 0)
                    return false;
                _status = TSTATUS::STOP;
                return true;
            }
            return false;
        }

        void Detach()
        {
            EnableDetach();
            pthread_detach(_tid);
        }

        bool IsJoinable() { return _joinable; }

        string Name() { return _name; }
        ~Thread()
        {
        }

    private:
        string _name;
        pthread_t _tid;
        pid_t _pid;
        bool _joinable; // 是否分离,默认不是
        func_t<T> _func;
        TSTATUS _status;
        T _data;
    };

}

#endif
相关推荐
木古古1821 分钟前
搞一个高效的c/c++开发环境,工具VIm+自研vim插件+Shell脚本
linux·编辑器·vim
茫忙然1 小时前
U 盘搭建免驱 Linux 便携系统教程
linux·服务器
一起逃去看海吧2 小时前
dify-03
java·linux·开发语言
fengyehongWorld2 小时前
Linux 根据端口进行的相关查询
linux
lihao lihao2 小时前
linux匿名管道
linux·运维·服务器
うちは止水2 小时前
weston出图调试
linux·wayland·weston
STDD2 小时前
Farming Simulator 25(模拟农场 25) Linux 专服搭建完全指南
linux·运维·javascript
好好风格3 小时前
宝塔面板 HTTPS 端口证书不生效排查记录
linux·运维·nginx
用户2367829801684 小时前
Linux pgrep 命令详解:按名称查找进程 PID 的高效方法
linux