1.线程栈

        对于 Linux 进程或者说主线程,简单理解就是main函数的栈空间,在fork的时候,实际上就是复制了父亲的 stack 空间地址,然后写时拷贝(cow)以及动态增长。如果扩充超出该上限则栈溢出会报段错误(发送段错误信号给该进程)。
        进程栈是唯一可以访问未映射页而不一定会发生段错误---超出扩充上限才报。
例子:栈位于非标准内存区域(如自定义内存分配器)触发条件:栈未使用系统默认的栈区域(如高地址向下增长),而是通过手动分配内存模拟栈行为。
        然而对于主线程生成的子线程而言,其 stack 将不再是向下生长的,而是事先固定下来的。线程栈⼀般是调用glibc/uclibc等的 pthread 库接口 pthread_create 创建的线程,在文件映射区(或称之为共享区)。
        这种stack不能动态增长,一旦用尽就没了,这是和生成进程的fork不同的地方。对于子线程的 stack ,它其实是在进程的地址空间中map出来的⼀块内存区域
总结:

2.线程封装

模拟C++封装原生线程库pthread

版本一

#pragma once

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

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

namespace quitesix
{
    static int number = 1;
    class Thread
    {
    private:
        void EnableRunning()
        {
            _isrunning = true;
        }
        void EnableDetach()
        {
            _isdetach = true;
        }
        // 静态成员函数原因:非静态成员函数第一个参数是隐含的this指针,参数不匹配
        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)
            : _func(func)
            , _isdetach(false)
            , _isrunning(false)
            , _res(nullptr)
        {
            _name = "thread-" + std::to_string(number++);
        }
        ~Thread()
        {
        }

        bool Start()
        {
            if (_isrunning)
                return false;
            EnableRunning();
            // 传递this指针,因为静态成员函数不存在this指针,需要访问this指针中_func进行回调
            int n = pthread_create(&_tid, nullptr, Routine, this);
            if (n != 0)
            {
                std::cerr << "线程创建失败," << strerror(n) << std::endl;
                return false;
            }
            std::cout << "线程创建成功!" << std::endl;
            if (_isdetach)
                Detach();
            return true;
        }
        void Stop()
        {
            if (_isrunning)
            {
                int n = pthread_cancel(_tid);
                if (n != 0)
                {
                    std::cerr << "线程取消失败," << strerror(n) << std::endl;
                    return;
                }
                std::cout << "线程取消成功" << std::endl;
                _isrunning = true;
            }
        }
        void Detach()
        {
            // 已经分离了,直接退
            if (_isdetach)
                return;
            // 在运行且没有分离,分离
            if (_isrunning)
                pthread_detach(_tid);
            EnableDetach();
            std::cout << "线程分离成功" << std::endl;
        }
        void Join()
        {
            if (!_isrunning)
                return;
            int n = pthread_join(_tid, nullptr);
            if (n != 0)
            {
                std::cerr << "线程等待失败," << strerror(n) << std::endl;
                return;
            }
            std::cout << "线程等待成功" << std::endl;
        }

    private:
        pthread_t _tid;    // 线程ID
        func_t _func;      // 线程要执行的事务
        bool _isdetach;    // 是否分离
        bool _isrunning;   // 线程是否运行
        std::string _name; // 线程名
        void *_res;        // 返回值
    };
}

注意:Routine函数设置的是静态成员函数,原因:静态成员函数参数第一个不包含隐式的this指针,为了符合pthread_create函数要求的 void *(*)(void *)类型。

Rountine函数中为了能访问类的成员变量,采用参数接受this指针的方式,pthread_create函数的参数传递this指针。

pthread_setname_np和pthread_getname_np可以用来设置线程的名字,用于debug。

版本二

若要实现线程事务可传参,可以用类模板的形式实现。

线程局部存储

语法:在类型前加__thread

线程局部存储作用:全局变量,我不想这个变量让其他线程看到

线程局部存储:只能存储内置类型和部分指针

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐