Linux:线程封装

发布于:2025-09-13 ⋅ 阅读:(21) ⋅ 点赞:(0)


一、线程封装初步

1. 代码(完整)

#include "Thread.hpp"
#include <vector>
#include <unistd.h>

using namespace ThreadModule;

int main()
{
    std::vector<Thread> threads;
    for (int i = 0; i < 10; i++)
    {
        threads.emplace_back([]()
                             {
        while (true)
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name));
            std::cout << "我是一个新线程我的名字是" << name << std::endl;
            sleep(1);
            return nullptr;
        } });
    }

    for (auto &t : threads)
    {
        t.Start();
    }

    for (auto &t : threads)
    {
        t.Join();
    }

    return 0;
}
#ifndef _THREAD_H_
#define _THREAD_H_

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

namespace ThreadModule
{
    static uint32_t number = 1;

    class Thread
    {
        using func_t = std::function<void()>;

    private:
        void EnableDetach()
        {
            std::cout << "线程被分离了" << std::endl;
            _isdetach = true;
        }

        void EnableRunning()
        {
            _isrunning = true;
        }

        static void *Routine(void *args)
        {
            Thread* self = static_cast<Thread*>(args);
            self->EnableRunning();

            if (self->_isdetach)
                self->Detach();

            pthread_setname_np(self->_tid, self->_name.c_str());

            self->_func();
            return nullptr;
        }

    public:
        Thread(func_t func)
            : _tid(0), _isdetach(false), _isrunning(false), res(nullptr), _func(func)
        {
            _name = "Thread" + std::to_string(number++);
        }

        ~Thread() {}

        void Detach()
        {
            if (_isdetach)
            {
                return;
            }
            if (_isrunning)
            {
                pthread_detach(_tid);
            }
            EnableDetach();
        }

        bool Start()
        {
            if (_isrunning)
            {
                return false;
            }

            int n = pthread_create(&_tid, nullptr, Routine, this);
            if (n != 0)
            {
                std::cerr << "pthread_create error: " << strerror(n) << std::endl;
                return false;
            }
            else
            {
                std::cout << "Thread " << _name << " started." << std::endl;
                return true;
            }
        }

        bool Stop()
        {
            if (_isrunning)
            {
                int n = pthread_cancel(_tid);
                if (n != 0)
                {
                    std::cerr << "pthread_cancel error: " << strerror(n) << std::endl;
                    return false;
                }
                else
                {
                    _isrunning = true;
                    std::cout << "Thread " << _name << " stopped." << std::endl;
                    return true;
                }
            }

            return false;
        }

        void Join()
        {
            if (_isdetach)
            {
                std::cout << "你的线程已经是分离的了,不能进行join" << std::endl;
                return;
            }

            int n = pthread_join(_tid, &res);
            if (n != 0)
            {
                std::cerr << "create thread error: " << strerror(n) << std::endl;
            }
            else
            {
                std::cout << "join success" << std::endl;
            }
        }

    private:
        pthread_t _tid;
        std::string _name;
        bool _isdetach;
        bool _isrunning;
        void *res;
        func_t _func;
    };

}
#endif

2. 为什么 Routine 要写成静态函数(不能有 this 指针)

pthread_create 要求回调类型是 void*(*)(void*)(C 风格函数指针)。类的非静态成员函数有隐含的 this 参数,签名不匹配,因此不能直接作为 pthread_create 的入口。常见而正确的做法是:

  • 将入口写成 static 或全局函数;
  • this(或对象指针)作为 pthread_create 的第四个参数传进去;
  • Routine 里把 void* 转回 Thread*,再调用成员方法和成员数据。

3. std::function 的回调逻辑与注意点

使用 std::function<void()> 的优点:

  • 接口简单,支持 lambda、function、bind 等各种可调用对象;
  • 上层只需给出可执行任务,线程类负责生命周期管理。

在这里插入图片描述


4. pthread_setname_np / pthread_getname_np

可以把线程名字设置进去,然后通过get再获取回来

在这里插入图片描述


二、使用模板封装

1. 代码(完整)

#include "Thread.hpp"
#include <unistd.h>

using namespace ThreadModule;

class ThreadData
{
public:
    pthread_t tid;
    std::string name;
};

void Count(ThreadData td)
{
    int a = 5;
    while (a--)
    {
        std::cout << "我是一个新线程" << std::endl;
        sleep(1);
    }
}

int main()
{
    ThreadData td;
    Thread<ThreadData> t(Count, td);

    t.Start();

    t.Join();

    t.Start();
    t.Detach();
    sleep(5);

    t.Stop();

    sleep(5);

    t.Join();

    return 0;
}
#ifndef _THREAD_H_
#define _THREAD_H_

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

namespace ThreadModule
{
    static uint32_t number = 1;

    template<typename T>
    class Thread
    {
        using func_t = std::function<void(T)>;

    private:
        void EnableDetach()
        {
            std::cout << "线程被分离了" << std::endl;
            _isdetach = true;
        }

        void EnableRunning()
        {
            _isrunning = true;
        }

        static void *Routine(void *args)
        {
            Thread<T>* self = static_cast<Thread<T>*>(args);
            self->EnableRunning();

            if (self->_isdetach)
                self->Detach();

            pthread_setname_np(self->_tid, self->_name.c_str());

            self->_func(self->_data);
            return nullptr;
        }

    public:
        Thread(func_t func, T data)
            : _tid(0), _isdetach(false), _isrunning(false), res(nullptr), _func(func), _data(data)
        {
            _name = "Thread" + std::to_string(number++);
        }

        ~Thread() {}

        void Detach()
        {
            if (_isdetach)
            {
                return;
            }
            if (_isrunning)
            {
                pthread_detach(_tid);
            }
            EnableDetach();
        }

        bool Start()
        {
            if (_isrunning)
            {
                return false;
            }

            int n = pthread_create(&_tid, nullptr, Routine, this);
            if (n != 0)
            {
                std::cerr << "pthread_create error: " << strerror(n) << std::endl;
                return false;
            }
            else
            {
                std::cout << "Thread " << _name << " started." << std::endl;
                return true;
            }
        }

        bool Stop()
        {
            if (_isrunning)
            {
                int n = pthread_cancel(_tid);
                if (n != 0)
                {
                    std::cerr << "pthread_cancel error: " << strerror(n) << std::endl;
                    return false;
                }
                else
                {
                    _isrunning = true;
                    std::cout << "Thread " << _name << " stopped." << std::endl;
                    return true;
                }
            }

            return false;
        }

        void Join()
        {
            if (_isdetach)
            {
                std::cout << "你的线程已经是分离的了,不能进行join" << std::endl;
                return;
            }

            int n = pthread_join(_tid, &res);
            if (n != 0)
            {
                std::cerr << "create thread error: " << strerror(n) << std::endl;
            }
            else
            {
                std::cout << "join success" << std::endl;
            }
        }

    private:
        pthread_t _tid;
        std::string _name;
        bool _isdetach;
        bool _isrunning;
        void *res;
        func_t _func;
        T _data;
    };

}
#endif

2. 为什么用模板

模板化的 Thread<T> 允许你把“要传入线程的参数”封装成任意类型 T,再在 Routine 调用时把该 T 传给用户提供的回调函数。这种方式适合参数结构固定、希望一次性把参数打包传入的场景(例如 ThreadData)。

优点:

  • 接口清晰:构造器传 funcdata,线程里直接用 data
  • 对于小结构体或 POD 非常方便。

3. 更通用的做法:参数包与完美转发(简单示意)

如果想让线程接受任意数量和类型的参数,可用可变参数模板 + 完美转发。示意(简洁版):

template<typename Func, typename... Args>
class ThreadVar {
public:
    ThreadVar(Func&& f, Args&&... args) 
      : _task(std::bind(std::forward<Func>(f), std::forward<Args>(args)...)) {}

    bool Start() {
        pthread_create(&_tid, nullptr, &ThreadVar::Routine, this);
    }

private:
    static void* Routine(void* arg) {
        auto* self = static_cast<ThreadVar*>(arg);
        self->_task(); // 执行绑定后的闭包
        return nullptr;
    }
    std::function<void()> _task;
};

或者更高级用 std::tuple + std::applyRoutine 里做完美转发,避免不必要拷贝。总之:模板能把运行时开销降到最小,但实现复杂度提高。


三、线程的局部存储

1. 代码(完整)

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

// 该count叫做线程的局部存储!
__thread int count = 1;
// 线程局部存储有什么用?全局变量,我又不想让这个全局变量被其他线程看到!
// 线程局部存储,只能存储内置类型和部分指针

std::string Addr(int &c)
{
    char addr[64];
    snprintf(addr, sizeof(addr), "%p", &c);
    return addr;
}

void *routine1(void *args)
{
    (void)args;
    while (true)
    {
        std::cout << "thread - 1, count = " << count << "[我来修改count], "
                  << "&count: " << Addr(count) << std::endl;
        count++;
        sleep(1);
    }
}

void *routine2(void *args)
{
    (void)args;
    while (true)
    {
        std::cout << "thread - 2, count = " << count
                  << ", &count: " << Addr(count) << std::endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, nullptr, routine1, nullptr);
    pthread_create(&tid2, nullptr, routine2, nullptr);

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);

    return 0;
}

2. __thread 是什么

__thread 声明的变量在每个线程有一份独立的实例 — 换句话说,每个线程看到的是自己那份变量,其他线程看不到你这份的值(也就是“__thread只有自己才能看见”)。这正是线程局部存储(TLS,Thread Local Storage)的含义。

在你的示例中,count 在两个线程里各自有独立地址和值,输出会显示不同的 &count,和值互不干扰 —— 非常直观地证明了 TLS 的行为。🧠


3. 使用场景与注意

  • 适用于线程本地计数器、日志上下文(thread id、trace id)、缓存等。


网站公告

今日签到

点亮在社区的每一天
去签到