worker thread

本文讲述了在使用工作线程(worker thread)时,为何不应直接操作GUI对象,以防止死锁。作者通过具体例子解释了死锁发生的原因,并提供了使用PostMessage发送用户定义消息到主线程来解决这个问题的方法。尽管如此,作者在尝试将工作线程改为UI线程时仍然遇到挂起问题,最终发现WaitForSingleObject导致了死锁。解决方案是让工作线程获取数据,然后通过PostMessage通知UI线程更新控件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近用worker thread用的比较频繁,找到了一篇讲workthread讲的非常好的blog,请戳这里

ps,那篇blog写的非常好,但是你能想象吗?!这是作者2001写的,15年了

再结合我最近碰到的问题说两句,以下是blog中提到的

Worker threads and the GUI II: Don't touch the GUI

That's right. A worker thread must not touch a GUI object. This means that you should not query the state of a control, add something to a list box, set the state of a control, etc.

Why?

Because you can get into a serious deadlock situation. A classic example was posted on one of the discussion boards, and it described something that had happened to me last year. The situation is this: you start a thread, and then decide to wait for the thread to complete. Meanwhile, the thread does something apparently innocuous, such as add something to a list box, or, in the example that was posted, calls FindWindow. In both cases, the process came to a screeching halt, as all threads deadlocked.

Let's analyze these two situations in some detail, so you get a sense of what happened.

In my case, the list box sent a notification, via SendMessage, to its parent. This means the message went to its parent thread. But the parent thread was blocked, waiting for the thread to complete. But the thread couldn't complete until it could run, and guess what: the SendMessage was a cross-thread SendMessage, which would not return until it was processed. But the only thread that could process it was blocked, and couldn't run until the thread completed. Welcome to the world ofdeadlock.

How do you get around these problems?

In my case, I had just been sloppy. I actually know better, and have written as much. So much for being an expert. The workaround in almost all cases is that you must never to a SendMessage to a GUI object. In very rare cases, you can do aPostMessage, although this usually won't work because you need to pass in a pointer to a string, and cannot tell when the operation has finished so you can't tell when to release the string. Any string you pass in must be allocated either in static storage (as a constant), or on the heap. If it is allocated in writeable static storage, or on the heap, you need to know when the value can be reused or freed. PostMessage does not allow you to do this.

Therefore, the only solution is to use a user-defined message, and post it to the main GUI thread (usually the main frame, but frequently a CView-derived object). The main GUI thread then handles the SendMessage to the GUI object, and, knowing then when the operation has completed, is able to free up any resources.


而我呢,也恰恰碰到了跟博主类似的问题,我也是在一个workthread里处理数据,处理完了之后用回掉函数回调给CView,为listctrl add item,平常一切正常,但是在我wait工作线程结束的时候,就会hang,hang在listctrl这里,后来我就学习了博主,用PostMessage,但是我得wait event呀,等listctrl加完item再event通知我,但是我wait这个event的时候,奇怪还是会hang,猜测可能跟我跨dll有关吧,最后没办法,只得改成一旦得知wait,工作线程就不要做任何跟GUI有关的操作,就OK了。

其实博主的另一篇还推荐了另外以各种办法SendMessageTime,如下所示:

DWORD result;
if(!SendMessageTimeout(wnd->m_hWnd,         // target window
                       UWM_QUERY_SOMETHING, // message
		       0,                   // WPARAM
                       0,                   // LPARAM
                       SMTO_ABORTIFHUNG |
                       SMTO_NORMAL,
		       TIMEOUT_INTERVAL,
                       &result))
     { /* error or timed out */
      // take some appropriate action on timeout or failure
      // if we care, we can distinguish timeout from other
      // errors
      if(::GetLastError() == 0)
         { /* time out */
          // take timeout action
         } /* time out */
      else
         ( /* other error */
          // take error action
         } /* other error */
     } /* error or timed out */
  else
     { /* successful */
      // decode the result
      switch(result)
         { /* result */
          case ...: 
		break;
          case ...:
                break;
         } /* result */
      } /* successful */

这个方法我也试了,确实不会hang,即便我把time out的时间改为INFINITE,也还是不会hang,原因是设了这个参数SMTO_ABORTIFHUNG,它会主动避免hang的情况就不等了,但是会引起别的问题,比如主线程稍微弹个Message都会影响SendMessageTime,可能就发不成功,明明可以发过来的呀!!所有这个函数不好用,也可能是我没耐心,没仔细研究它的用法。

后来,我又有另外一个工作线程,也是用到listctrl,wait的时候就会hang,但这次我用PostMessage给CView发消息,让它帮忙处理就不会hang,也不知道咋回事,可能是因为我这个工作线程在主工程里头吧,也可能是因为我这回没wait event,总之就没hang

虽然问题是解决了,但是用work thread处理GUI还是不太好,等下回有时间了,就把这个work thread改成UI thread.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

终于有机会把这个workthread 改成UIthread,可惜还是会hang,hang的原因就是WaitForSingleObject(m_hThread, INFINITE);这句导致的,主线程在等工作线程结束,工作线程在访问UI,工作线程访问UI都是通过SendMessage的方式,所以就被hang死了。

Every access to a control from aworker thread is implemented as a SendMessage call.  The worker threadblocks when it makes this call.  Windows then waits for the UI thread topump messages, then completes the SendMessage in the UI thread context. By putting WaitForSingleObject in the UI thread you assure that it will notaccept messages, so deadlock results.

You can use the worker thread toacquire the data, put then you should PostMessage to the UI thread to have itput the data in the control.

 

源文档 <https://social.msdn.microsoft.com/Forums/en-US/b85127aa-3860-413a-b71c-d3664dc3222e/waitforsingleobject-deadlock-in-mfc-event-handler?forum=vcmfcatl


解决办法参照:http://www.programmer-club.com.tw/ShowSameTitleN/vc/25625.html

	DWORD nWaitCount=1;
	HANDLE *hWaitArray=&m_hThread;
	DWORD rc;
	do {	
		rc=MsgWaitForMultipleObjects(nWaitCount, hWaitArray, FALSE, INFINITE, QS_ALLEVENTS);
		if( (WAIT_OBJECT_0+nWaitCount)==rc ) { // got message
			MSG msg;
			if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
		}
	}while( WAIT_OBJECT_0!=rc );

查了下<<Windowsvic C/C++>>  Page281 如下解释:

A thread thatcreates windows and performs user-interface related tasks should use MsgWaitForMultipleObjectsExinstead of WaitForMultipleObjects because the latter prohibits the thread's user-interface fromresponding to the user.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值