Linux OS:线程封装 | RAII封装锁 | 随机数运算任务封装

Linux OS:线程封装 | RAII封装锁 | 随机数运算任务封装

一、Linux OS:线程封装

1.1 线程私有成员

 现在我们手动封装一个线程,其中类成员变量包含:线程id、线程名、线程运行状态(是否允许)、线程数据、创建线程的回调方法!具体如下:

cpp 复制代码
template <class T>
using func_t = std::function<void(T)>; //回调方法重命名

template <class T>
class Thread
{
private:
    pthread_t _tid; // 线程id
    std::string _threadname; // 线程名
    bool _isrunning; // 线程运行状态(是否允许)
    func_t<T> _func; // 创建出的新线程执行该回调函数
    T _data; // 线程数据
};

1.2 线程成员函数

 对于线程我们将实现下面几个成员方面:构造函数初始化相关数据(新创建的线程没有启动运行)、启动线程接口、线程等待接口、获取线程名!

1)构造函数初始化相关数据

 对于构造函数,我们通过外部用户传入线程名、执行方法、线程数据!

c 复制代码
Thread(const std::string& threadname, func_t<T> func, T data)
        :_tid(0), _threadname(threadname), _isrunning(false)
        	, _func(func), _data(data)
    {}

2)启动线程接口

 线程默认是没有运行的,我们通过在启动线程接口调用pthread_create函数来创建线程,通过构造函数获取到的数据,将相关数据传递给pthread_create函数。

c 复制代码
static void *ThreadRoution(void *args)
{
    Thread *ts = static_cast<Thread *>(args);
    ts->_func(ts->_data);
}

void Start()
{
    int n = pthread_create(&_tid, nullptr, ThreadRoution, this);
    if(n == 0)
    {
        _isrunning = true;
    }
}

 为什么我们需要这样设计,在创建时将执行函数设计为静态的,并将this指针作为参数传递给线程执行函数?

 原因在于编译器会自动传递一个this指针给类成员函数,并且是第一个参数!所以我们将线程执行函数设计为静态的,静态函数会存在this指针。但我们需要在该函数中调用线程对象的执行方法,所以我们将类对象(this)作为参数传给静态函数!之后在对参数强转即可获得线程对象,然后既可以调用相关执行方法了!

3)线程等待

 只有对启动的线程进行等待才是有意义的!这也是我们为啥在类中添加表示线程状态的成员变量的原因之一!

c 复制代码
void Join()
{
    if(_isrunning)
    {
        int n = pthread_join(_tid, nullptr);//等待线程,等待成功返回0
        if(n == 0)
            _isrunning = false;
    }
}

4)获取线程名

c 复制代码
const std::string& GetThreadName()
{
    return _threadname;
}

1.3 总体代码

c 复制代码
template <class T>
using func_t = std::function<void(T)>;

template <class T>
class Thread
{
public:
    Thread(const std::string& threadname, func_t<T> func, T data)
        :_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data)
    {}

    static void *ThreadRoution(void *args)
    {
        Thread *ts = static_cast<Thread *>(args);
        ts->_func(ts->_data);
    }

    void Start()
    {
        int n = pthread_create(&_tid, nullptr, ThreadRoution, this);
        if(n == 0)
        {
            _isrunning = true;
        }
    }

    void Join()
    {
        if(_isrunning)
        {
            int n = pthread_join(_tid, nullptr);
            if(n == 0)
                _isrunning = false;
        }
    }
    const std::string& GetThreadName()
    {
        return _threadname;
    }
    ~Thread()
    {}
private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

二、RAII封装锁

 在实际项目中,一般不建议用户自己持有锁,释放锁。这样做的后果就是:一旦加锁和解锁之间的代码抛异常,将会导致死锁,进而导致内存泄漏!

 所以我们一般采用RAII思想。对于临界区加锁时,我们将锁交给对象(LockGuard)来管理。其中LockGuard只存在一个锁成员函数。用户将锁传递给LockGuard对象后,构造函数初始化列表时将锁的对象创建出来,然后在函数体中进行加锁,在析构函数中封装解锁方法!

c 复制代码
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock):_lock(lock)
    {}
    void Lock()
    {
        pthread_mutex_lock(_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(_lock);
    }
    ~Mutex()
    {}

private:
    pthread_mutex_t *_lock;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock): _mutex(lock)
    {
        _mutex.Lock();
    }
    ~LockGuard()
    {
        _mutex.Unlock();
    }
private:
    Mutex _mutex;
};

三、随机数运算任务疯转

 下面我们疯转一个类:我们传入两个值和运算符后对数据进行处理。但运算过程中,用户传入运算符可能未知,可能出现除0,模0错误...所以除了运算结果外,我们添加一个_code的成员变量,用于判断结构是否可信!

 然后我们用一个枚举集合表示结果集合(0表示结果可信,非0对应相应错误)。然后我们不仅可以通过_code成员变量判断结果是否可行,一旦结果不可信还可以获取到出错原因。

 这里我们提供了打印任务和打印运算结果接口、仿函数等接口。比较简单,就不多说了!

c 复制代码
const int defaultResult = 0;
enum
{
    ok = 0,
    div_zero,
    mod_zero,
    unknow
};

class Task
{
public:
    Task()
    {}
    Task(int data_x, int data_y, char op)
        : _data_x(data_x), _data_y(data_y), _op(op), _result(ok), _code(defaultResult)
    {
    }

    void Run()
    {
        switch (_op)
        {
        case '+':
            _result = _data_x + _data_y;
            break;
        case '-':
            _result = _data_x - _data_y;
            break;
        case '*':
            _result = _data_x * _data_y;
            break;
        case '/':
        {
            if (_data_y == 0)
                _code = div_zero;
            else
                _result = _data_x / _data_y;
            break;
        }
        case '%':
        {
            if (_data_y == 0)
                _code = mod_zero;
            else
                _result = _data_x % _data_y;
            break;
        }
        default:
            _code = unknow;
        }
    }

    void operator()()
    {
        Run();
    }

    std::string PrintTask()
    {
        std::string s;
        s += std::to_string(_data_x);
        s += _op;
        s += std::to_string(_data_y);
        s += "=?";
        return s;
    }

    std::string PrintResult()
    {
        std::string s;
        s += std::to_string(_data_x);
        s += _op;
        s += std::to_string(_data_y);
        s += "=";
        s += std::to_string(_result);
        s += ", [";
        s += std::to_string(_code);
        s += "]";
        return s;
    }

    ~Task()
    {
    }

private:
    int _data_x;
    int _data_y;
    char _op;

    int _result;
    int _code; // 错误码为0,结果可行,否则不可信
};
相关推荐
用户9718356334669 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
夜悊9 小时前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴10 小时前
CMake 021: IF 条件判据详诠
c++·cmake
猪脚踏浪10 小时前
linux 拷贝文件或目录到指定的位置
linux
_wyt0011 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
大树881 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠1 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质1 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush41 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5201 天前
Linux 11 动态监控指令top
linux