vc中ui线程和工作线程协调通讯的朴素实现方式

当要处理一些比较耗时的任务时, 一般是把这些任务放到一个工作线程中去执行, 否则会阻塞界面的响应, 导致用户体验差....

所以经常会用到线程

从任务角度上看, 线程大致分两类, 界面线程(UI线程, 一般由主线程充当); 工作线程(有UI线程创建, 根据生命周期, 可再细分为长期线程和临时线程)

vc中, 创建线程可以通过多种方式, 这里说

线程就是一个可以和其他线程"并发"执行的函数, 一个随便写的例子:

unsigned __stdcall _netWorkThread(void *pVoid)
{
    cout << "network thread running" <<endl;
    NetWorkThread* pNetWork = reinterpret_cast<NetWorkThread*>(pVoid);

    HANDLE events[] = {pNetWork->QuitEvent(), pNetWork->ConnEvent(), pNetWork->SendEvent()};
    bool bRun = true;
    while(bRun)
    {
        DWORD dwRet = ::WaitForMultipleObjects(sizeof(events) / sizeof(events[0]), events, FALSE, pNetWork->EventTimeOut());
        switch(dwRet)
        {
        case WAIT_TIMEOUT:            //等待超时
            cout << "time out" <<endl;
            break;
        case WAIT_FAILED:            //错误
            //GetLastError();
            break;
        case WAIT_OBJECT_0:            //退出事件
            bRun = false;
            break;
        case WAIT_OBJECT_0 + 1:        //连接事件
            if(!pNetWork->TaskConn())
            {
                pNetWork->FeedBack()->ConnFailed();
            }
            break;
        case WAIT_OBJECT_0 + 2:        //发送数据包事件
            break;
        }
    }

    cout << "network thread quit" <<endl;
    return 0;
}
_netWorkThread函数是全局函数(不知道如何封装成一个成员函数)

class NetWorkThread
{
public:
    NetWorkThread();
    virtual ~NetWorkThread();

    bool run(IFeedBack* iFeedBack);

    HANDLE& QuitEvent();
    HANDLE& ConnEvent();
    HANDLE& SendEvent();
    int EventTimeOut();

    void Conn(CString ip, unsigned short port);
    void Quit();
    CString ServerIP();
    unsigned short ServerPort();

    //只被工作线程调用, Task前缀
    bool TaskConn();

    //反馈工作线程执行结果的接口对象
    IFeedBack* FeedBack();

private:
    bool CheckEvent();        //检测事件是否正确


private:
    HANDLE hQuitEvent;        //退出线程事件
    HANDLE hConnEvent;        //连接服务器事件
    HANDLE hSendEvent;        //发送数据包事件
    int eventTimeOut;        //事件超时(单位毫秒)
    
    
    HANDLE hThread;            //线程句柄


    SOCKET sock;            //连接套接字
    CString serverIP;        //服务器ip
    unsigned short serverPort;    //服务器端口

    IFeedBack* iFeedBack;    //ui线程接口
};
NetWorkThread::NetWorkThread(): 
    eventTimeOut(10000), 
    sock(INVALID_SOCKET), 
    iFeedBack(0)
{
    WSADATA wsaData;

    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if(iResult != NO_ERROR)
    {
        cout << "WSAStartup Error" <<endl;
    }
    hQuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    hConnEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    hSendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
}

bool NetWorkThread::CheckEvent()
{
    if(    NULL == hQuitEvent ||
        NULL == hConnEvent ||
        NULL ==    hSendEvent)
        return false;
    return true;
}

NetWorkThread::~NetWorkThread()
{
    if(NULL != hQuitEvent)
        CloseHandle(hQuitEvent);
    if(NULL != hConnEvent)
        CloseHandle(hConnEvent);
    if(NULL != hSendEvent)
        CloseHandle(hSendEvent);
}

bool NetWorkThread::run(IFeedBack* iFeedBack)
{
    if(!CheckEvent())
        return false;

    this->iFeedBack = iFeedBack;
    
    unsigned uRet;
    hThread = (HANDLE)_beginthreadex(0, 0, _netWorkThread, this, 0, &uRet);
    if(0 == hThread)
    {
        return false;
    }
    return true;
}

HANDLE& NetWorkThread::QuitEvent()
{
    return this->hQuitEvent;
}

HANDLE& NetWorkThread::ConnEvent()
{
    return this->hConnEvent;
}

HANDLE& NetWorkThread::SendEvent()
{
    return this->hSendEvent;
}
int NetWorkThread::EventTimeOut()
{
    return this->eventTimeOut;
}

void NetWorkThread::Conn(CString ip, unsigned short port)
{
    this->serverIP = ip;
    this->serverPort = port;
    SetEvent(this->hConnEvent);
}
void NetWorkThread::Quit()
{
    SetEvent(this->hQuitEvent);
}
CString NetWorkThread::ServerIP()
{
    return this->serverIP;
}
unsigned short NetWorkThread::ServerPort()
{
    return this->serverPort;
}

bool NetWorkThread::TaskConn()
{
    sockaddr_in addr_in;
    addr_in.sin_family = AF_INET;
    addr_in.sin_addr.s_addr = inet_addr(this->serverIP.GetBuffer(0));
    addr_in.sin_port = htons(this->serverPort);

    this->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(INVALID_SOCKET == this->sock)
    {
        cout << "call socket function error:" << WSAGetLastError() <<endl;
        return false;
    }
    
    int ret = connect(this->sock, (sockaddr*)&addr_in, sizeof(addr_in));
    if(SOCKET_ERROR == ret)
    {
        cout << "WSaGetLastError:" << WSAGetLastError() <<endl;
        return false;
    }
    return true;
}

IFeedBack* NetWorkThread::FeedBack()
{
    return this->iFeedBack;
}

写着写着, 不知道要写什么了, 晕~~~~~~~~

思路:

UI线程到工作线程的消息传递, 可以通过 封装队列, 加个事件触发功能

工作线程到UI线程的反馈, 可以是UI线程先继承一个接口并实现这些接口, 工作线程保存UI线程对象的接口类型指针, 当UI线程要反馈消息到UI线程时候, 就可以通过这个指针对象调用接口方法, 这样UI界面就可以反馈结果给用户

这样做可能存在危险, 因为这个反馈的动作是工作线程调用的, 如果此时UI线程也同样要操作界面, 就会造成线程间数据冲突了, 要做同步的话, 就得考虑不要卡主UI线程..........

T____T

原文地址:https://www.cnblogs.com/jianc/p/2874351.html