C++线程模型 one-loop-per-thread

C++11引入了线程对象,使我们能够在语言层面方便的执行线程操作,能够将成员函数,函数对象,lambda函数都当作线程入口,从而不用再去在不同平台上对系统API去做一些生硬的兼容措施(比如将类指针当作线程参数传入到线程函数中从而能够访问类成员)。在这里,我对C++11的线程对象进行简单的封装,实现的简单的线程操作,接着辅助于上一篇提到的消息队列,实现了支持消息转发的线程模型。首先看下简单线程模型的封装:

#ifndef HEADER_RUNNABLE
#define HEADER_RUNNABLE

#include 

class CRunnable
{
public:
    CRunnable() : _stop(false) {}
    virtual ~CRunnable() {}

    //线程基本操作
    virtual void Start() {
        _stop = false;
        if (!_pthread) {
            _pthread = std::shared_ptr<std::thread>(new std::thread(std::bind(&CRunnable::Run, this)));
        }
    }
    virtual void Stop() {
        _stop = true;
    }
    virtual void Join() {
        if (_pthread) {
            _pthread->join();
        }
    }

    //线程主逻辑
    virtual void Run() = 0;

    bool GetStop() {
        return _stop;
    }

    static void Sleep(int interval) {
        std::this_thread::sleep_for(std::chrono::milliseconds(interval));
    }

protected:
    CRunnable(const CRunnable&) = delete;
    CRunnable& operator=(const CRunnable&) = delete;

protected:
    bool                            _stop;
    std::shared_ptr<std::thread>    _pthread;
};
#endif

在start 函数中创建线程对象,只要继承这个类并在run中实现线程逻辑,就可在另一个线程中执行。在one-loop-per-thread中,我们的主逻辑类继承之,在run函数中循环从消息队列中提取操作处理。下面我们继承这个类添加一个消息队列:

#ifndef HEADER_RUNNABLEALONETASKLIST
#define HEADER_RUNNABLEALONETASKLIST

#include "Runnable.h"
#include "TaskQueue.h"

typedef std::function<void()> Task;
template
class CRunnableAloneTaskList : public CRunnable
{
public:
    CRunnableAloneTaskList() {}
    virtual ~CRunnableAloneTaskList() {}

    int GetTaskListSize() {
        return _task_list.Size();
    }

    //线程消息投递
    void Push(const T&& t) {
        _task_list.Push(t);
    }
    void Push(const T& t) {
        _task_list.Push(t);
    }

    //线程主逻辑
    virtual void Run() = 0;

protected:
    T _Pop() {
        return std::move(_task_list.Pop());
    }

    CRunnableAloneTaskList(const CRunnableAloneTaskList&) = delete;
    CRunnableAloneTaskList& operator=(const CRunnableAloneTaskList&) = delete;

private:
    CTaskQueue           _task_list;         //每个线程都有自己的任务队列
};
#endif

继承这个类的派生类在run中循环的 从消息队列中Pop任务执行,其他线程往这个类中投递消息只需调用Push函数即可(消息队列是线程安全的),接着我们实现一个每个线程都单独有消息队列并且支持往指定线程投递消息的的类:

#ifndef HEADER_RUNNABLEALONETASKLISTWITHPOST
#define HEADER_RUNNABLEALONETASKLISTWITHPOST

#include 
#include 

#include "Runnable.h"
#include "TaskQueue.h"

typedef std::function<void()> Task;

class CRunnableAloneTaskListWithPost : public CRunnable
{
public:
    CRunnableAloneTaskListWithPost() {}
    virtual ~CRunnableAloneTaskListWithPost() {}

    int GetTaskListSize() {
        return _task_list.Size();
    }

    virtual void Start();
    virtual void Stop();

    //线程消息投递
    void Push(const Task&& func) {
        _task_list.Push(func);
    }
    void Push(const Task& func) {
        _task_list.Push(func);
    }

    //线程主逻辑
    virtual void Run();

    std::thread::id GetId()const { return _id; }

    //向指定线程投递任务
    static  bool PostTask(const std::thread::id& thread_id, const Task& func);

private:
    Task _Pop() {
        return std::move(_task_list.Pop());
    }

    CRunnableAloneTaskListWithPost(const CRunnableAloneTaskListWithPost&) = delete;
    CRunnableAloneTaskListWithPost& operator=(const CRunnableAloneTaskListWithPost&) = delete;

private:
    CTaskQueue        _task_list;         //每个线程都有自己的任务队列
    std::thread::id         _id;

    static  std::mutex                                                  _map_mutex;     //_runnable_map访问锁
    static  std::map<std::thread::id, CRunnableAloneTaskListWithPost*>  _runnable_map;  //记录线程对象,支持线程间消息投递
};

std::mutex CRunnableAloneTaskListWithPost::_map_mutex;
std::map<std::thread::id, CRunnableAloneTaskListWithPost*> CRunnableAloneTaskListWithPost::_runnable_map;

void CRunnableAloneTaskListWithPost::Start() {
    _stop = false;
    if (!_pthread) {
        _pthread = std::shared_ptr<std::thread>(new std::thread(std::bind(&CRunnableAloneTaskListWithPost::Run, this)));
    }

    Push([this]() {
        std::unique_lock<std::mutex> lock(_map_mutex);
        auto iter = _runnable_map.find(std::this_thread::get_id());
        if (iter == _runnable_map.end()) {
            _runnable_map[std::this_thread::get_id()] = this;
            _id = std::this_thread::get_id();
        }
    });
}

void CRunnableAloneTaskListWithPost::Stop() {
    Push([this]() {
        {
            std::unique_lock<std::mutex> lock(_map_mutex);
            auto iter = _runnable_map.find(std::this_thread::get_id());
            if (iter != _runnable_map.end()) {
                _runnable_map.erase(iter);
            }
        }
        Push(nullptr);
        _stop = true;
    });
}

bool CRunnableAloneTaskListWithPost::PostTask(const std::thread::id& thread_id, const Task& func) {
    std::unique_lock<std::mutex> lock(_map_mutex);
    auto iter = _runnable_map.find(thread_id);
    if (iter != _runnable_map.end()) {
        if (iter->second) {
            iter->second->Push(func);
            return true;
        }
    }
    return false;
}

void  CRunnableAloneTaskListWithPost::Run() {
    while (!_stop) {
        auto t = _Pop();
        if (t) {
            t();

        } else {
            break;
        }
    }
}
#endif

我们用一个静态的map来存储创建了的所有线程类,然后通过指定线程id来向指定的线程投放执行任务。这里有点特殊的是存储和释放map中函数对象的时候需要创建个匿名函数投递到线程中去操作,因为只有这样才能使get_id函数获取到的线程id是当前要操作的线程id。接下来我们再实现一个类公用一个消息队列,竞争消息队列中的执行任务:

#ifndef HEADER_RUNNABLESHARETASKLIST
#define HEADER_RUNNABLESHARETASKLIST

#include 
#include 

#include "TaskQueue.h"

typedef std::function<void()> Task;

template<typename T = Task>
class CRunnableShareTaskList : public CRunnable
{
public:
    explicit CRunnableShareTaskList(int channel);
    virtual ~CRunnableShareTaskList();

    int GetTaskListSize() {
        std::unique_lock<std::mutex> lock(_map_mutex);
        return _task_list_map[_channel].first->Size();
    }

    //线程消息投递
    void Push(const T&& func) {
        std::unique_lock<std::mutex> lock(_map_mutex);
        _task_list_map[_channel].first->Push(func);
    }

    void Push(const T& func) {
        std::unique_lock<std::mutex> lock(_map_mutex);
        _task_list_map[_channel].first->Push(func);
    }

    //线程主逻辑
    virtual void Run() = 0;

private:
    T _Pop() {
        return std::move(_task_list_map[_channel].first->Pop());
    }

    CRunnableShareTaskList(const CRunnableShareTaskList&) = delete;
    CRunnableShareTaskList& operator=(const CRunnableShareTaskList&) = delete;

private:
    int                             _channel;
    bool                            _stop;
    std::shared_ptr<std::thread>    _pthread;

    static  std::mutex                                                          _map_mutex;     //_runnable_map访问锁
    static  std::map<int, std::pair<std::shared_ptr>, int>>       _task_list_map; //共享任务队列 channel TaskQueue TaskQueue的共享个数
};

template<typename T>
std::mutex CRunnableShareTaskList::_map_mutex;
template<typename T>
std::map<int, std::pair<std::shared_ptr>, int>> CRunnableShareTaskList::_task_list_map;

template<typename T>
CRunnableShareTaskList::CRunnableShareTaskList(int channel) : _stop(false), _channel(channel) {
    std::unique_lock<std::mutex> lock(_map_mutex);
    auto iter = _task_list_map.find(_channel);
    if (iter != _task_list_map.end()) {
        iter->second.second++;

    } else {
        _task_list_map[_channel] = std::make_pair(std::shared_ptr>(new CTaskQueue), 1);
    }
}

template<typename T>
CRunnableShareTaskList::~CRunnableShareTaskList() {
    std::unique_lock<std::mutex> lock(_map_mutex);
    auto iter = _task_list_map.find(_channel);
    if (iter != _task_list_map.end()) {
        iter->second.second--;
        if (iter->second.second == 0) {
            _task_list_map.erase(iter);
        }
    }
}
#endif

用一个map保存公用的消息队列信息,用channel来区分共享的消息队列。这两个类可能有点想当然了,并没有考虑到多少实际的使用场景。
老惯例,下边是测试使用代码:

#include "RunnableShareTaskList.h"
#include "RunnableAloneTaskListWithPost.h"
int main() {
    CRunnableAloneTaskListWithPost run[2];
    run[0].Start();
    run[1].Start();

    CMemaryPool* pool = NULL;
    Task task = [&pool]() {
        if (pool) {
            delete pool;
        }
    };
    run[0].Push([&pool]() {
        pool = new CMemaryPool; 
        int a = 0;
        a++;
    });

    run[1].Push([&pool, task]() { 
        CRunnableAloneTaskListWithPost::PostTask(pool->GetCreateThreadId(), task);
    });

    run[0].Join();
    run[1].Join();
}

int main() {
    CRunnableShareTaskList<> run1(1);
    CRunnableShareTaskList<> run2(1);
    run1.Start();
    run2.Start();

    CMemaryPool* pool = NULL;
    Task task = [&pool]() {
        if (pool) {
            delete pool;
        }
    };
    run1.Push([&pool]() {
        pool = new CMemaryPool;
        int a = 0;
        a++;
    });

    run1.Push([&pool]() {
        CRunnableShareTaskList<>::Sleep(1000);
        if (pool) {
            delete pool;
        }
    });

    run1.Stop();
    run2.Join();
}

CRunnableShareTaskList 的对象是创建不出来的,得派生实现run函数~~.接下来我们通过继承线程类来实现一个实际的例子 日志线程,这个放到下一篇中说……
GitHub:https://github.com/caozhiyi/Base

你可能感兴趣的:(thread)