梁肇新《编程高手箴言》94页内容:

很多人在使用线程的时候,都喜欢在线程内画图,程序就会很容易出错,而且还是那种没有任何响应和提示的错误问题。

例如,如下是一个文件复制的程序,这个程序由两个线程组成,一个是复制文件的线程,另一个是显示文件复制进度的过程。当文件复制一部分后,进度条就向前移动一点。理论上,这个程序没有什么问题。但是,这个程序有一个很大的隐患,即主程序也可能某一时刻要更新这个进度条。例如,进度被其他窗口挡住后或者整个窗口放大缩小时,整个窗口就要刷新,这时,线程的那个部分也要刷新它,操作系统也要刷新它。这样,三个部分都要去刷新它,程序就很容易死锁。

这时会什么响应也没有了。这种问题在多线程中是很常见的。那怎么处理这个问题呢?

有一条原则,即程序中的线程一概不直接操作线程部分中的GDI。它只要发一个消息给主程序,让主程序来绘制图形,就不会出现任何的问题。

发送消息的方法就是用PostMessage。但一定不能用SendMessage。因为用PostMessage可以让主程序去调度绘图,而SendMessage会立即去绘制图形。
         上面所说不是太明白。
         是不是包含这种情况:在一个线程内,调用某个窗口的Invalidate,然后再调用UpdateWindow,那么这样做是有隐患的,因为UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。这个重绘可能与系统正在进行的重绘发生冲突,导致死锁。如果没有使用UpdateWindow,那么就是安全的。因为在Invalidate后,在WM_PAINT消息被处理后,系统会调用OnPaint进行窗口刷新,系统会协调这个刷新和系统正在进行的刷新,因此是安全的。          因为我现在的一个程序有问题,一直不能解决,怀疑和这个方面有关。
          还请高手们解惑。多谢。

解决方案 »

  1.   

    三个部分都要去刷新它不会发生死锁,windows会把这些WM_PAINT合成为一个WM_PAINT然后发送给对应线程
      

  2.   

    我越看越觉得他讲的没道理,你最好看一下msdn然后自己试一下
      

  3.   


    那么想请教一下,UpdateWindow可以使WM_PAINT被直接发送给目标窗口,从而导致窗口立即重绘,这个怎么理解?假设系统正在执行OnPaint,这个UpdateWindow引起的OnPaint是等系统将正在执行的OnPaint执行完再执行,还是“不顾一切”地执行自己的OnPaint,即与系统正在执行的OnPaint并行执行?如果是前一种情况,那么应该是没有问题的,后一种情况是否会引起问题?
    多谢回复。
      

  4.   

    出什么错误了?GDI最后也得走UI线程啊。
      

  5.   

    这个UpdateWindow引起的OnPaint是等系统将正在执行的OnPaint执行完再执行“不顾一切”地执行自己的OnPaint,即与系统正在执行的OnPaint并行执行,这完全不可能
      

  6.   


    The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty. The function sends a WM_PAINT message directly to the window procedure of the specified window, bypassing the application queue. 按照这段文字(msdn),这个消息是被送到窗口过程的,那么就是立即被执行的,也就是说不会再和别的WM_PAINT合并在一起,再确定执行?不知道是否这样理解?多谢回复。
      

  7.   


    那么系统在执行OnPaint之前是否要检测是否有OnPaint正在执行,如果有,那就必须等这个正在执行的OnPaint执行完毕再执行?多谢。
      

  8.   

    被send的message不是被立即执行,要等到GetMessage或PeekMessage开始处理非队列消息
      

  9.   


    我刚才看了一下孙鑫的书,21页的窗口过程函数,根据消息的类型直接处理各个消息,GetMessage和PeekMessage是在另外的函数执行(例如WinMain)。我的理解是这样的,调用Invalidate,而不调用UpdateWindow,其发出的WM_PAINT进入消息队列,其后的处理是规范的,标准化的,其可靠性是由系统保障的。如果调用UpdateWindow,那么WM_PAINT的处理是不标准的,不规则的,其可靠性是不能保证的,可能造成线程的死锁。直接的原因是多个OnPaint被并行执行,并且OnPaint由于内部的原因,例如不可重入,导致出现问题。这个纯粹是我的猜想,还请各位指点。
      

  10.   

    如果调用UpdateWindow,那么WM_PAINT的处理是不标准的,不规则的,其可靠性是不能保证的,可能造成线程的死锁。直接的原因是多个OnPaint被并行执行,并且OnPaint由于内部的原因,例如不可重入,导致出现问题。
    这个不对,如果需要重绘,UpdateWindow等到WM_PAINT被处理完再返回,InvalidateRect把矩形设为无效然后返回,系统在合适的时候发送WM_PAINT如果调用UpdateWindow,那么WM_PAINT的处理是标准的,也是规则的,其可靠性是可以保证的,不太可能成线程的死锁。多个OnPaint被不会并行执行,因为一个线程不可能同时执行多个程序。
      

  11.   

    如果调用UpdateWindow,那么WM_PAINT的处理是不标准的,不规则的,其可靠性是不能保证的,可能造成线程的死锁。直接的原因是多个OnPaint被并行执行,并且OnPaint由于内部的原因,例如不可重入,导致出现问题。
    ============
    不可能的,不可能有多个OnPaint并行执行的。
      

  12.   

    类似的问题我遇到过
    问题不在于绘制本身
    而在于绘制时对相关数据处理
    但就WM_PAINT消息肯定是要在消息队列里等待的
    可是如果在发送这个消息之前主线程和辅助线程都在处理绘制数据而此时没有互斥控制就容易出问题
      

  13.   

     如果窗口更新的区域不为空,UpdateWindow函数通过发送一个WM_PAINT消息来更新指定窗口的客户区。函数绕过应用程序的消息队列,直接发送WM_PAINT消息给指定窗口的窗口过程,如果更新区域为空,则不发送消息。
      

  14.   

    谢谢各位热心。现在证明,程序中出现的异常是由于硬件问题引起的。一块PCIE接口卡有问题。
    多谢。