【Linux】线程概念与控制(4)_线程封装

hello~ 很高兴见到大家! 这次带来的是Linux系统中关于线程这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙


文章目录


一、代码实现

  1. 下面是 thread.hpp 文件代码,是对 一些 pthread 函数的封装操作:
cpp 复制代码
#pragma once

#include <pthread.h>
#include <functional>
#include <iostream>

namespace ThreadMoudle
{
    int gnumber = 1;
    using callback_t = std::function<void()>;
    enum class TSTATUS
    {
        THREAD_NEW,
        THREAD_RUNNING,
        THREAD_STOP
    };

    std::string IsJoined(bool joinable)
    {
        return joinable ? "Joinable" : "Detached";
    }
    std::string Status2String(TSTATUS s)
    {
        switch (s)
        {
        case TSTATUS::THREAD_NEW:
            return "THREAD_NEW";
        case TSTATUS::THREAD_RUNNING:
            return "THREAD_RUNNING";
        case TSTATUS::THREAD_STOP:
            return "THREAD_STOP";
        default:
            return "UNKNOWN";
        }
    }
    class Thread
    {
    private:
        void ToRunning()
        {
            _status = TSTATUS::THREAD_RUNNING;
        }
        void ToStop()
        {
            _status = TSTATUS::THREAD_STOP;
        }
        //线程要执行的函数,必须加上static,不加第一个参数会是this指针
        static void *Routine(void *args)
        {
            Thread *it = static_cast<Thread*>(args);
            //线程要执行的任务   
            it->_ct();
            pthread_setname_np(it->_tid, it->_name.c_str());
            it->ToStop();

            return nullptr;
        }

    public:
        Thread(callback_t ct)
            : _tid(-1), _status(TSTATUS::THREAD_NEW), _ct(ct), _joinable(true), _result(nullptr)
        {
            _name = "New-Thread-" + std::to_string(gnumber++);
        }
        bool Start()
        {
            //把this传递给要执行的函数
            int n = pthread_create(&_tid, nullptr, Routine, this);
            ToRunning();

            if (n != 0)
                return false;
            return true;
        }
        void Join()
        {
            if (_joinable)
            {
                int n = pthread_join(_tid, &_result);
                if (n != 0)
                {
                    std::cerr << "join error:" << n << std::endl;
                    return;
                }
                (void)_result;
                ToStop();
            }
            else
            {
                std::cerr << "join error, joinable: " << IsJoined(_joinable) << std::endl;
            }
        }
        void Die()
        {
            if (_status == TSTATUS::THREAD_RUNNING)
            {
                pthread_cancel(_tid);
                ToStop();
            }
        }
        void Detach()
        {
            if (_joinable)
            {
                pthread_detach(_tid);
                _joinable = false;
            }
            else
            {
                std::cerr << "detach " << _name << "failed" << std::endl;
            }
        }
        ~Thread()
        {
        }
        void Print()
        {
            std::cout << "_name: " << _name << std::endl;
            std::cout << "tid: " << _tid << std::endl;
            std::cout << "status: " << Status2String(_status) << std::endl;
            std::cout << "joinable: " << IsJoined(_joinable) << std::endl;
        }

    private:
        std::string _name;//线程名字
        pthread_t _tid;//线程id
        TSTATUS _status;//线程的执行状态

        callback_t _ct;//线程要执行的任务
        bool _joinable;//线程是否可以被等待
        void* _result;//线程的返回值
    };
}
  1. 之所以 Routine 函数必须声明为静态(加上 static),是因为如果不加 static,它就属于类的非静态成员函数,会隐式携带 this 指针作为第一个参数,导致函数签名与 pthread_create 要求的线程入口函数格式不匹配。pthread_create 要求线程入口函数必须是无 this 指针、参数为单个 void*、返回值为 void* 的普通函数指针格式。而我们后续访问类的非静态私有成员时又必须用到 this 指针,因此在调用 pthread_create 时,就把当前对象的 this 指针以 void* 类型作为参数传递给这个静态 Routine 函数,再在函数内部还原出对象指针,从而正常访问类成员

二、解决传参问题

1.1 使用模板

  1. 可以使用模板来解决传参问题,下面是使用单个模板参数解决传递单参数的方法,如果要传递多参数可以使用可变参数模板。要修改 thread.hpp 文件。
cpp 复制代码
#pragma once

#include <pthread.h>
#include <functional>
#include <iostream>

namespace ThreadMoudle
{
    int gnumber = 1;
    template<typename T>
    using callback_t = std::function<void(T&)>;
    enum class TSTATUS
    {
        THREAD_NEW,
        THREAD_RUNNING,
        THREAD_STOP
    };

    std::string IsJoined(bool joinable)
    {
        return joinable ? "Joinable" : "Detached";
    }
    std::string Status2String(TSTATUS s)
    {
        switch (s)
        {
        case TSTATUS::THREAD_NEW:
            return "THREAD_NEW";
        case TSTATUS::THREAD_RUNNING:
            return "THREAD_RUNNING";
        case TSTATUS::THREAD_STOP:
            return "THREAD_STOP";
        default:
            return "UNKNOWN";
        }
    }

    template<typename T>
    class Thread
    {
    private:
        void ToRunning()
        {
            _status = TSTATUS::THREAD_RUNNING;
        }
        void ToStop()
        {
            _status = TSTATUS::THREAD_STOP;
        }
        //线程要执行的函数,必须加上static,不加第一个参数会是this指针
        static void *Routine(void *args)
        {
            Thread *it = static_cast<Thread<T>*>(args);
            //线程要执行的任务   
            it->_ct(it->_data);
            pthread_setname_np(it->_tid, it->_name.c_str());
            it->ToStop();

            return nullptr;
        }

    public:
        Thread(callback_t<T> ct, const T& data)
            : _tid(-1)
            , _status(TSTATUS::THREAD_NEW)
            , _ct(ct)
            , _joinable(true)
            , _result(nullptr)
            , _data(data)
        {
            _name = "New-Thread-" + std::to_string(gnumber++);
        }
        bool Start()
        {
            //把this传递给要执行的函数
            int n = pthread_create(&_tid, nullptr, Routine, this);
            ToRunning();

            if (n != 0)
                return false;
            return true;
        }
        void Join()
        {
            if (_joinable)
            {
                int n = pthread_join(_tid, &_result);
                if (n != 0)
                {
                    std::cerr << "join error:" << n << std::endl;
                    return;
                }
                (void)_result;
                ToStop();
            }
            else
            {
                std::cerr << "join error, joinable: " << IsJoined(_joinable) << std::endl;
            }
        }
        void Die()
        {
            if (_status == TSTATUS::THREAD_RUNNING)
            {
                pthread_cancel(_tid);
                ToStop();
            }
        }
        void Detach()
        {
            if (_joinable)
            {
                pthread_detach(_tid);
                _joinable = false;
            }
            else
            {
                std::cerr << "detach " << _name << "failed" << std::endl;
            }
        }
        ~Thread()
        {
        }
        void Print()
        {
            std::cout << "_name: " << _name << std::endl;
            std::cout << "tid: " << _tid << std::endl;
            std::cout << "status: " << Status2String(_status) << std::endl;
            std::cout << "joinable: " << IsJoined(_joinable) << std::endl;
        }

    private:
        std::string _name;//线程名字
        pthread_t _tid;//线程id
        TSTATUS _status;//线程的执行状态

        callback_t<T> _ct;//线程要执行的任务
        bool _joinable;//线程是否可以被等待
        void* _result;//线程的返回值

        T _data;
    };
}

1.2 使用类和lambda表达式

  1. 我们可以把任务和它所涉及的一些变量打包成一个类,然后通过传递无参无返回值的lambda表达式(匿名函数对象)给目标线程,从而实现传递多个参数。这样就不用修改我们封装的 thread.hpp 文件了。
cpp 复制代码
#include <iostream>
#include "Thread.hpp"
#include <unistd.h>
#include <vector>

class Task
{
public:
    Task(int x, int y) : _x(x), _y(y)
    {}
    void Execute()
    {
        _result = _x + _y;
    }
    void Print()
    {
        std::cout << _x <<  " + " << _y << " = " << _result << std::endl; 
    }
    ~Task()
    {}
private:
    int _x;
    int _y;
    int _result;
};



int main()
{
    Task tk(12, 20);
    ThreadMoudle::Thread thread([&tk](){tk.Execute();});
    thread.Start();
    thread.Join();
    thread.Print();
    sleep(5);
    tk.Print();
    return 0;
}

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

相关推荐
AlfredZhao2 天前
生产环境里,为什么不建议把普通端口直接暴露到公网?
linux·https·443·80
戴为沐3 天前
Linux内存扩容指南
linux
zylyehuo4 天前
Linux 彻底且安全地删除文件
linux
用户805533698034 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
用户034095297914 天前
linux fcitx 5 雾凇拼音 设置在中文输入法下仍然输入英文标点
linux
Web3探索者6 天前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh
zylyehuo6 天前
Linux系统中网线与USB网络共享冲突
linux
Sokach10157 天前
Linux Shell 脚本从零到能用:一个新手的一天学习总结
linux
AlfredZhao8 天前
Docker 容器时区不对,`timedatectl` 不存在怎么办?
linux·timezone
zzzzzz3109 天前
9K Star 炸裂开源!这个 C 语言写的代码知识图谱,把 Linux 内核索引压缩到了 3 分钟
linux·服务器·sql