TApplicaiton.ProcessMessages不能在非主线程使用
来源:互联网 发布:python input split 编辑:程序博客网 时间:2024/06/08 07:05
本文仅是实验和实证的结果.(深层原因需要有机会了解ProcessMessages的实现)
1.缘起
Hotfox在移植到BCB环境下,以支持客户端开发后,在应用开发测试时发现:移植的本地管理模块在增加角色时会阻塞在一个简单的ShowMessage调用上.程序执行流程大致如下:
- 生成发送570-Request请求
- 执行同步调用,消息处理者为TFrmAddRole::Do572函数(CFormMsgHandler类型)
- 在TFrmAddRole::Do572函数中,处理成功提示时阻塞.(测试时在函数入口增加一行ShowMessage语句即导致阻塞)
2.解决思路
阻塞的原因是消息的处理是由后台线程调用的(bbox_c模块的HandleInput),且消息处理函数包含了VCL操作。目标是满足客户端开发要求,并且不想影响现有的应用代码。需要把后台线程的函数调用在框架层次转移到主线程上执行。
以下是对插件和TForm消息处理函数的转移执行的说明:.
对于CFormMsgHandler,
用
SendMessage(CBasePlugInModule::async_fc_wnd_,WM_ASYNC_FUNC_CALL,2,(LPARAM)new CFormFuncCallPara(form_,func_,msg->Duplicate()));
取代
(form_->*func_)(msg);
对于插件,用:
SendMessage(async_fc_wnd_,WM_ASYNC_FUNC_CALL,1,(LPARAM)new CPluginFuncCallPara(fp,p->mod_,in,&out,&or));
取代:
code = (p->mod_->*fp)(in, out,or);
(异步化一词来自最初的准备采用PostMessage的想法,已不合适,)
异步化函数调用消息由内部的TfrmAsyncFuncCall窗体处理.处理代码如下:
void __fastcall TfrmAsyncFuncCall::OnReceive(TMessage &Message){ switch(Message.WParam) { case 1: { CPluginFuncCallPara *fcp = (CPluginFuncCallPara*)Message.LParam; fcp->Call(); delete fcp; } break; case 2: { CFormFuncCallPara *fcp = (CFormFuncCallPara*)Message.LParam; fcp->Call(); delete fcp; } break; case 3: { CShowMessagePara *fcp = (CShowMessagePara*)Message.LParam; ///< @todo 外部指定如何显示信息,以适应不同的应用 MessageShow(0,fcp->text_.c_str(),fcp->caption_.c_str(),MSGERROR); delete fcp; } break; }}
迁移处理的相关定义如下:
#define WM_ASYNC_FUNC_CALL WM_USER+2///< AsyncFuncCall: 异步化函数调用////< WM_FUNC_CALL用于在多线程环境下把可能需要访问VCL对象的函数进行异步化处理的自定义窗口消息///< WPARAM : 1-插件函数 2-Form函数 3-信息提示///< LPARAM :根据WPARAM对应不同的结构体对象//---------------------------------------------------------------------------///< 插件函数异步调用消息参数struct CPluginFuncCallPara { MSGFUNC fp_; CPlugInModule *mod_; CWrappedMsg<> *in_; vector<CWrappedMsg<> *> *out_; DISPATCH_RESULT *or_; CPluginFuncCallPara(MSGFUNC fp,CPlugInModule *mod,CWrappedMsg<> *in,vector<CWrappedMsg<> *> *out,DISPATCH_RESULT *or): fp_(fp),mod_(mod),in_(in),out_(out),or_(or) { } int Call() { return (mod_->*fp_)(in_, *out_,*or_); };};//---------------------------------------------------------------------------///< Form函数异步调用消息参数struct CFormFuncCallPara { TForm *form_; FormMsgHandleFunc func_; ///< 消息处理函数 CMsg *msg_; CFormFuncCallPara(TForm *form,FormMsgHandleFunc func,CMsg *msg):form_(form),func_(func),msg_(msg) { } int Call() { return (form_->*func_)(msg_); }};
3.验证
测试发现修改后仍然阻塞,发生在以下位置(对于插件处理函数)SendMessage(async_fc_wnd_,WM_ASYNC_FUNC_CALL,1,(LPARAM)new CPluginFuncCallPara(fp,p->mod_,in,&out,&or));
没有进入到TfrmAsyncFuncCall::OnReceive中.
4.再究
测试发现由于同步调用导致:int ret = CBasePluginModule::sc_->call(msg,result,0,0,timeout);
同步调用发生在窗体上,主线程等待服务器的返回并处理后唤醒。
在处理返回的消息时SendMessage的消息没有机会被处理。
需要使用TApplication的ProcessMessage来中断当前操作,处理消息队列中的消息。
由于同步调用是另起一个线程(do_syncall_func)通过条件变量等待返回的消息被处理后被唤醒或超时或取消的。代码如下:
/// 执行同步调用int HTX_Syncall::call(CMsg *msg,int &result,CMsg **ppmsg,SC_CALLBACK fp,int tv) { HANDLER_THREAD_INFO *ti = prepare(msg); SC_THREAD_PARA tp; tp.sc = this; tp.tv = tv; tp.result = 0; tp.thr_ = ACE_Thread::self(); ACE_hthread_t hthread; ACE_Thread::spawn(do_syncall_func,&tp,THR_NEW_LWP|THR_JOINABLE,0,&hthread); { ACE_GUARD_RETURN(ACE_Thread_Mutex,guard,ti->lock_ready_cond_var_,-1); ti->ready_cond_var_->wait();///< 等待do_syncall_func进入wait } if (fp) (*fp)(msg); else { HTX_NETWORK::instance()->SendMsg(msg); } ACE_Thread::join(hthread); threads_.unbind(ti->cmd_id_); ///< 从等待队列中清除 delete ti; result = tp.result; if (ppmsg) { *ppmsg = tp.ret_msg_; } else { if (tp.ret_msg_) tp.ret_msg_->Release(); } return tp.wait_result;}
在do_syncall_func函数中加入Application->ProcessMessage(通过回调cb_func_设置)后,仍然阻塞.
是否意味着,Application->ProcessMessage不能在非主线程中使用?
5.试验ProcessMessages
目的是确认在非主线程中执行ProcessMessage没有预期效果.试验方案:
主线程中创建2个线程,主线程组塞。
创建的2个线程,一个线程PostMessage或SendMessage,一个线程执行Applicaiton->ProcessMessage.
情形1:
DWORD __stdcall TestThreadProc(void *arg) { while(1) { Application->ProcessMessages(); Sleep(1000); }; return 0;}DWORD __stdcall TestThreadProc2(void *arg) { while(1) { SendMessage(form2->Handle,WM_ASYNC_FUNC_CALL,0,0); ///< 阻塞在此 Sleep(30); } return 0;}void __fastcall TForm1::Button3Click(TObject *Sender){ CreateThread(0,0,TestThreadProc,0,0,0); CreateThread(0,0,TestThreadProc2,0,0,0); while(1) { Sleep(1000); };}
情形2:正常
DWORD __stdcall TestThreadProc(void *arg) { while(1) { /// Application->ProcessMessages(); ///< 不论是否有此行都正常 Sleep(1000); }; return 0;}DWORD __stdcall TestThreadProc2(void *arg) { while(1) { SendMessage(form2->Handle,WM_ASYNC_FUNC_CALL,0,0); Sleep(30); } return 0;}void __fastcall TForm1::Button3Click(TObject *Sender){ CreateThread(0,0,TestThreadProc,0,0,0); CreateThread(0,0,TestThreadProc2,0,0,0); while(1) { Application->ProcessMessages(); Sleep(1000); };}
结论:TApplication的ProcessMessages在非主线程中执行没有作用.
6.再试
修改同步调用函数,使ProcessMessages在主线程上执行。
代码修改成如下内容时,问题得以解决.
/// 执行同步调用int HTX_Syncall::call(CMsg *msg,int &result,CMsg **ppmsg,SC_CALLBACK fp,int tv) { HANDLER_THREAD_INFO *ti = prepare(msg); SC_THREAD_PARA tp; tp.sc = this; tp.tv = tv; tp.result = 0; tp.thr_ = ACE_Thread::self(); ACE_hthread_t hthread; ACE_Thread::spawn(do_syncall_func,&tp,THR_NEW_LWP|THR_JOINABLE,0,&hthread); { ACE_GUARD_RETURN(ACE_Thread_Mutex,guard,ti->lock_ready_cond_var_,-1); ti->ready_cond_var_->wait();///< 等待do_syncall_func进入wait } if (fp) (*fp)(msg); else { HTX_NETWORK::instance()->SendMsg(msg); }#ifdef HTX_WINDOWS ///< 对于客户端程序,在主线程上的同步调用会导致窗口消息的阻塞.如果返回的消息在其它线程(如HTX_Scheduler任务线程)处理过程中,使用SeneMessage导致死锁 ///< 在此情况下,cb_func_的作用是处理消息队列中的消息(BORLANDC中执行Application->ProcessMessage) ///< @note 在do_syncall_func函数中执行cb_func_没有效果,因为****Application->ProcessMessage不能在非主线程中执行**** do { DWORD status = 0; if (!GetExitCodeThread((void*)hthread,&status)) break; (*cb_func_)(0); if (status!=STILL_ACTIVE) { break; } Sleep(1000); } while(1);#else ACE_Thread::join(hthread);#endif threads_.unbind(ti->cmd_id_); ///< 从等待队列中清除 delete ti; result = tp.result; if (ppmsg) { *ppmsg = tp.ret_msg_; } else { if (tp.ret_msg_) tp.ret_msg_->Release(); } return tp.wait_result;}
- TApplicaiton.ProcessMessages不能在非主线程使用
- android:在非主线程中不能够使用Toast.makeText
- ProgressDialog不能在非主线程中show
- 在Android中,非主线程不能更新UI
- 在非主线程中不能操作主线程中的View
- 在非主线程中创建窗口
- 在非主线程中创建窗口
- 在非主线程中创建窗口
- 在非主线程运行NSURLConnection
- 在非主线程中创建窗口
- 在非主线程里处理bitmap
- 在非主线程中创建窗口
- 在非主线程中创建窗口
- 在主线程中不能连接网络
- android 非主线程内使用Looper
- Android 关于非主线程不能操作UI的认识
- iOS 非主线程不能执行UI操作
- 关于handler在非主线程中充当计时器使用的疑问
- lucene笔记____IndexReader和IndexWriter注意事项
- Java设计模式之观察者
- 在应用间利用KeyChain共享数据
- 代理模式(Proxy Pattern)
- 【Linux Tips】strace -f 可 fork 子进程
- TApplicaiton.ProcessMessages不能在非主线程使用
- 解决Sublime Text 2中文显示乱码问题
- 【最小生成树】POJ1789-Truck History-prim算法
- capwap学习笔记——初识capwap(五)
- android学习笔记---58_拖拉功能与多点触摸,实现图片的拖拉和缩放功能
- 重写与重载
- QML, Qt C++混合编程--QML与Qt C++ 交互机制探讨与总结
- 关于jboss热部署
- BGI Error:Graphics not initialized (use 'initgraph')真正详解