實現signal和slot機制(2)-跨線程投遞

前言

在1中,我們實現了一個基礎的signal+slot的模塊件調用機制,不過那個direct調用,在這篇中,我們將支持誇線程調用,即在對象的線程上下文中,調用對象的函數。

對象線程綁定

在qt中,有一個很重要的概念,對象線程綁定,即在每個對象實例時候,其保存其線程上下文信息,其中就有一個事件循環,跨線程的信號就是將這個一個task丟到這個事件循環中,在對象綁定的線程中完成slot響應。

實現

對slot調用的打包-Task

我們需要將一個跨線程的slot調用打包成一個task,然後丟到想相應的事件循環中。在事件循環中需要維護一個task隊列,那第一還是需要做容器。

struct TaskBase
{
    virtual ~TaskBase() = 0;
    virtual void run() = 0;
};

TaskBase::~TaskBase()
{
}

TaskBase爲我們Task基類,在事件循環中,維護其一個鏈表。
我們繼承它,實現我們第一個Task,一個一般的Task。

template <typename Method>
struct NormalTask : public TaskBase
{
    typedef typename ParameterTupleTraits<ParameterTraits<Method>>::Parameters Parameters;
    typedef typename TypeTraits<typename ParameterTraits<Method>::classType_value>::pointerType_value type_value;

    NormalTask(type_value object, Method m, Parameters paras) : TaskBase(),
                                                                m_object(object),
                                                                m_method(m),
                                                                m_paras(paras)
    {
    }

    virtual ~NormalTask() {}

    void run()
    {
        printf("run:%lu\n", pthread_self());
        impleDoTask(m_object, m_method, m_paras);
    }

    type_value m_object;
    Method m_method;
    Parameters m_paras;
};

其實一個模板,於具體的對象成員函數相關,接受,對象指針,對象成員函數指針和參數。pthread_self可以打印當前線程的標識,其實一個unsigned long類型的數,這裏爲了看其的執行線程。

事件循環和對象綁定

爲了實現對象線程綁定,我們必須提供一個在這套系統上的基類,正如qt中的QObject。

struct Box
{
    Box()
    {
        m_loop = EventLoop::currentEventLoop();
    }

    EventLoop *m_loop;
};

下面開始我們的事件循環,其實這隻有一個任務隊列,實際中其還必須能夠監聽系統各種事件,比如按鍵,鼠標,加其發送給合適的對象,就是我的一篇博客介紹的事件監聽分發,這裏不做討論。

 static EventLoop *currentEventLoop()
    {
        EventLoop *re = NULL;
        pthread_t pid = pthread_self();
        std::map<pthread_t, EventLoop *>::iterator ite = s_eventloops.find(pid);
        if (ite != s_eventloops.end())
        {
            re = ite->second;
        }
        else
        {
            re = new EventLoop(pid);
            s_eventloops.insert(std::pair<pthread_t, EventLoop *>(pid, re));
        }
        return re;
    }

    static void exit()
    {
        for (auto ite = s_eventloops.begin(); ite != s_eventloops.end(); ++ite)
        {
            ite->second->quit();
            delete (ite->second);
        }
        s_eventloops.clear();
    }

    int exec()
    {
        while (run)
        {
            for (auto ite = m_tasks.begin(); ite != m_tasks.end();)
            {
                (*ite)->run();
                delete (*ite);
                ite = m_tasks.erase(ite);
            }
            //sleep(10);
        }
        return 1;
    }

    void quit()
    {
        run = false;
    }
};

std::map<pthread_t, EventLoop *> EventLoop::s_eventloops = std::map<pthread_t, EventLoop *>();

怎麼實現投遞

提供了一個枚舉,來設置連接類型

enum CONNECT_TYPE
{
    SYNC,
    AYNC,
    AUTO
};

對應:同步,異步和系統決定。
在原來signaleemit時,我們必須實現區別發送

 void eemit()
    {
        printf("eemit:%lu\n", pthread_self());
        std::tuple<> para;
        for (typename listValue_type::iterator ite = sloters.begin(); ite != sloters.end(); ++ite)
        {
            CONNECT_TYPE type = (*ite)->m_type;
            switch (type)
            {
            case SYNC:
            {
                if (EventLoop::currentEventLoop() == ((*ite)->eventloop()))
                {
                    (*ite)->dotask(para);
                }
                else
                {
                    (*ite)->eventloop()->postNormalTask((*ite)->convertAnormalTask(para));
                }
            };
            break;
            case AYNC:
            {
                (*ite)->eventloop()->postNormalTask((*ite)->convertAnormalTask(para));
            };
            break;
            case AUTO:
            {
                if (EventLoop::currentEventLoop() == ((*ite)->eventloop()))
                {
                    (*ite)->dotask(para);
                }
                else
                {
                    (*ite)->eventloop()->postNormalTask((*ite)->convertAnormalTask(para));
                }
            };
            break;
            default:
                break;
            }
        }
    }

爲了實現在eemit中根據對象的線程綁定來區分行爲,但是我們得到的對象,只是一個基類指針,所以對原來的基類進行了改造。

template <typename Paras>
struct SlotBase
{
    SlotBase(CONNECT_TYPE type) : m_type(type)
    {
    }

    virtual TaskBase *convertAnormalTask(Paras paras) = 0;

    virtual EventLoop *eventloop() = 0;

    virtual ~SlotBase() = 0;
    virtual void dotask(Paras paras) = 0;
    CONNECT_TYPE m_type;
};

template <typename Paras>
SlotBase<Paras>::~SlotBase()
{
}

在slot實現這連個純虛函數,來得到打包好的Task和得到槽對象的事件循環指針。

  TaskBase *convertAnormalTask(Parameters paras)
    {
        return new NormalTask<M>(m_object, m_method, paras);
    }

    EventLoop *eventloop()
    {
        return m_object->m_loop;
    }

這樣保證了在eemit時獲得正確的task和事件循環,當槽對象的eventloop和eemit的對象不在一個線程中,我們就將task投遞到槽對象的eventloop中。
爲了實現想下列的使用

    EventLoop *loop = EventLoop::currentEventLoop();
    loop->postNormalTask(NewNormalTask(&object, &A::func_b, 1314));

我們提供了NewNormalTask各版本

template <typename Method>
TaskBase *NewNormalTask(typename TypeTraits<typename ParameterTraits<Method>::classType_value>::pointerType_value object,
                        Method m, typename ParameterTraits<Method>::P0 p0,
                        typename ParameterTraits<Method>::P1 p1,
                        typename ParameterTraits<Method>::P2 p2,
                        typename ParameterTraits<Method>::P3 p3,
                        typename ParameterTraits<Method>::P4 p4,
                        typename ParameterTraits<Method>::P5 p5)
{
    StaticCheck<ParameterTraits<Method>::size == 6> a;
    UNUSE(a);
    typename ParameterTupleTraits<ParameterTraits<Method>>::Parameters paras = std::make_tuple(p0, p1, p2, p3, p4, p5);
    return new NormalTask<Method>(object, m, paras);
}

運用實例

#include <iostream>
#include "signal.h"
using namespace std;

struct TTT
{
    int a;
    int b;
    int c;
};

class N : public Box
{
public:
    virtual void func_c(int a, int b) = 0;

};

class A : public N
{
public:
    A()
    {
        s1.connect(this, &A::func_ii);
    }
    void func_b(int a) {cout << a << "aaaaaa\n";}
    void func_c(int a, int b) {cout << a << "+" << b << "=" << a+b << std::endl;}
    void func_a()
    {
        TTT t = {1, 's', 't'};
        s.eemit(t, t, t);
    }


    void func_i()
    {
        int *i = new int(1);
        s1.eemit(i);
    }

    void func_ii(int *i)
    {
        cout << *i;
        (*i)++;
        delete i;
    }

    void func_z()
    {
        cout << "zhou xiang  ";

    }

    Signal<void (*)(TTT, TTT, TTT)> s;
    Signal<void (*)(int*)> s1;

};

class B : public Box
{
public:
    B(A *a):m_a(a){}
    B(){}
    void func_b(int a) {cout << a << "bbbbbbb\n";}
    void func_slot(TTT t, TTT t2, TTT t3)
    {
        cout << t.a + t2.b + t3.c << "-==-=-=-==-=\n";
    }

    void func_z()
    {
        cout << "love chenchen\n";
    }

    A *m_a;
    void func_e()
    {
        m_a->s.connect(this, &B::func_slot);
    }
};

void* pt_callback(void * arg)
{
    A aa;
    B bb;
    Signal<void (*)()> *s = (Signal<void (*)()>*)(arg);
    s->connect(&aa, &A::func_z);
    s->connect(&bb, &B::func_z);
    EventLoop *loop = EventLoop::currentEventLoop();
    loop->exec();
    return NULL;
}

int main()
{
    A object;
    B object1(&object);

    Signal<void (*)(int, int)> s;

    Signal<void (*)()> ssss;

    // ssss.connect(&object, &A::func_z);
    // ssss.connect(&object1, &B::func_z);
    // ssss.connect(&object1, &B::func_z);

//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    s.connect(&object ,&A::func_c);
//    object1.func_e();
//    object1.func_e();

//    object.func_a();

    //ssss.eemit();
    // ssss.disconnect(&object, &A::func_z);
    // ssss.disconnect(&object1, &B::func_z);


    //object.func_i();


    //cout << "---------------------------\n";

    //ssss.eemit();


    //s.eemit(4, 456);


    // EventLoop *loop = EventLoop::currentEventLoop();
    // loop->postNormalTask(NewNormalTask(&object, &A::func_b, 1314));
    //std::cout << "+++++++++++++++++++++\n";

    pthread_t ptid;

    int ptre = pthread_create(&ptid, NULL, pt_callback, (void *)(&ssss));
    if (ptre != 0)
    {
        printf("dispatcher thread create fail!\n");
    }

    ssss.eemit();
    // ssss.disconnect(&object, &A::func_z);
    // ssss.disconnect(&object1, &B::func_z);


    //object.func_i();


    cout << "---------------------------\n";

    ssss.eemit();

    EventLoop *loop = EventLoop::currentEventLoop();
    loop->postNormalTask(NewNormalTask(&object, &A::func_b, 1314));
    loop->exec();

    return 0;
}

運行結果
這裏寫圖片描述

在操作隊列的時候,沒有加鎖。還有就是線程之間執行不可控,在eemit時,在pt_callback中,connect還沒有完成,導致,有時有兩個響應有時一個,線程向標準輸出寫入的時候也是不可控的,所有log有點難看,但還是說明了問題,操作在對象的線程上下問中進行。

項目代碼可以在1中找到地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章