天天看点

[C/C++]建议在异步调用的函数的Callback中预留UserData参数

有时候会碰到一些的C++程序员定义的异步函数(通过Callback通知)没有UserData参数,这样会给OO设计带来很大的麻烦。举个例子(只是例子,不讨论有没有必要作成异步函数):

typedef void (*PFCALLBACK)();

DWORD WorkingFunc(LPVOID* param)

{

    // Do something...

    PFCALLBACK pFun = (PFCALLBACK)param;

    pFun ();

    return 0;

}

 void Asynchronous(PFCALLBACK pCallbak)

{

    HANDLE hThrd = CreateThread(NULL, 0, WorkingFunc, pCallbak, 0, NULL);

    CloseHandle(hThrd );

}

如果要调用Asynchronous这个函数,通常可以这么做:

class myClass

{

public:

    static void myCallback();

    void myFunction();

private:

    static HANDLE m_hEvent;

};

HANDLE myClass::m_hEvent= NULL:

void myClass::myCallback()

{

    SetEvent(m_hEvent);

}

void myClass::myFunction()

{

     m_hEvent= CreateEvent(NULL, FALSE, FALSE, NULL);

    Asynchronous(myCallback);

    // Do some thing

    WaitForSingleObject(m_hEvent, INFINITE);

    CloseHandle(m_hEvent);

    m_hEvent = NULL;

}

由于m_hEvent是静态成员变量,所有的类的对象都共用一个m_hEvent,所以当这些对象在不同线程中被调用时就可能造成错误!

所以,我们在设计使用Callback的异步函数时,应该预留UserData参数,这样,代码变成以下方式:

typedef void (*PFCALLBACK)(void* UserData);

struct ThreadParam

{

    PFCALLBACK pCallback;

    void* UserData;

};

DWORD WorkingFunc(LPVOID param)

{

    // Do something...

    ThreadParam* pParam = (ThreadParam*)param;

    pParam->pCallback(pParam->UserData);

    delete pParam;

    return 0;

}

 void Asynchronous(PFCALLBACK pCallbak, void* UserData)

{

    ThreadParam *pParam = new ThreadParam;

    pParam ->pCallback = pCallbak;

    pParam ->UserData = UserData;

    HANDLE hThrd = CreateThread(NULL, 0, WorkingFunc, pParam , 0, NULL);

    CloseHandle(hThrd);

}

而myClass可以实现成:

class myClass

{

public:

    static void myCallback(void* UserData);

    void myFunction();

};

void myClass::myCallback(void* UserData)

{

    HANDLE hHandle = (HANDLE)UserData

    SetEvent(hHandle);

}

void myClass::myFunction()

{

    HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    Asynchronous(myCallback, hEvent);

    // Do some thing

    WaitForSingleObject(hEvent, INFINITE);

    CloseHandle(hEvent);

}

由于去掉了静态成员变量,所以在多线程的环境下也能保证正确工作。

大家可以去参考微软的Callback的设计,几乎都留了UserData或者类似的参数。