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