我的问题是,如果一个线程快速将消息发布到主UI线程,并且如果我在那时更新UI,有时主消息队列会被卡住(我没有更好的词来描述这个).

这是简化的repro代码:

const
  TH_MESSAGE = WM_USER + 1; // Thread message
  TH_ParaM_ACTION = 1;
  TH_ParaM_FINISH = 2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    ThreadHandle: Integer;
    procedure ThreadMessage(var Message: TMessage); message TH_MESSAGE;
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function ThreadProc(Parameter: Pointer): Integer;
var
  ReceiverWnd: HWND;
  I: Integer;
  Counter: Integer;
begin
  Result := 0;
  ReceiverWnd := Form1.Handle;
  Counter := 100000;
  for I := 1 to Counter do
  begin
    PostMessage(ReceiverWnd,TH_MESSAGE,TH_ParaM_ACTION,I);
    //Sleep(1); // <- is this the cure?
  end;
  PostMessage(ReceiverWnd,TH_ParaM_FINISH,GetCurrentThreadID);
  OutputDebugString('Thread Finish OK!'); // <- I see this
  EndThread(0);
end;

procedure TForm1.ThreadMessage(var Message: TMessage);
begin
  case Message.WParam of
    TH_ParaM_ACTION:
      begin
        Label1.Caption := 'Action' + IntToStr(Message.LParam);
        //Label1.Update;
      end;
     TH_ParaM_FINISH:
       begin
         OutputDebugString('ThreadMessage Finish'); // <- Dose not see this
         Button1.Enabled := True;
         CloseHandle(ThreadHandle);
       end;
  end;    
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadId: LongWord;
begin
  Button1.Enabled := False;
  ThreadId := 1;
  ThreadHandle := BeginThread(nil,@ThreadProc,nil,ThreadId);
end;

我确实意识到工作线程循环非常繁忙.我认为,由于线程将消息发布到主UI线程,因此它(主UI线程)有机会在从工作线程接收其他消息时处理它的消息.
当我增加柜台时,问题就会升级.

问题:
除非我添加Label1.Update,否则我从未看到Label1正在更新;并且主UI被阻止.
TH_ParaM_ACTION永远不会达到100000(在我的情况下) – 随机超过90000.
TH_ParaM_FINISH永远不会进入消息队列.
显然cpu使用率非常高.

问题:
处理这种情况的正确方法是什么?从工作线程发布的消息是否从消息队列中删除(如果是,那么为什么)?
睡眠中的睡眠(1)真的可以解决这个问题吗?如果是,那为什么1? (0没有)

好.感谢@Sertac和@LU,我现在意识到消息队列有一个限制,现在使用ERROR_NOT_ENOUGH_QUOTA来检查PostMessage的结果.但是,主UI仍然没有响应!

function ThreadProc(Parameter: Pointer): Integer;
var
  ReceiverWnd: HWND;
  I: Integer;
  Counter: Integer;
  LastError: Integer;
  ReturnValue,Retry: Boolean;
begin
  Result := 0;
  ReceiverWnd := Form1.Handle;
  Counter := 100000;
  for I := 1 to Counter do
  begin
    repeat
      ReturnValue := PostMessage(ReceiverWnd,I);
      LastError := GetLastError;
      Retry := (not ReturnValue) and (LastError = ERROR_NOT_ENOUGH_QUOTA);
      if Retry then
      begin
        Sleep(100); // Sleep(1) is not enoght!!!
      end;
    until not Retry;
  end;
  PostMessage(ReceiverWnd,GetCurrentThreadID);
  OutputDebugString('Thread Finish OK!'); // <- I see this
  EndThread(0);
end;

仅供参考,这是我正在检查的原始代码:
Delphi threading by example

此示例搜索文件中的文本(同时5个线程).显然,当你创建这样的任务时,你必须看到所有匹配的结果(例如在ListView中).

问题是,如果我在meany文件中搜索,并且搜索字符串很短(如“a”) – 就会发现很多匹配.当FileStream.Read(Ch,1)= 1时忙碌的循环确实快速发布了消息(TH_FOUND)并且匹配并充满了
消息队列.

实际上没有到达消息队列的消息.正如@Sertac所提到的“默认情况下消息队列的限制为10000”.

来自MSDN PostMessage

There is a limit of 10,000 posted messages per message queue. This
limit should be sufficiently large. If your application exceeds the
limit,it should be redesigned to avoid consuming so many system
resources. To adjust this limit,modify the following registry key (USERPostMessageLimit)

正如其他人所说,这个代码/模式应该重新设计.

解决方法

您以大于处理消息的速率的速率充斥消息队列.最终队列变满了.

如果您绝对需要主线程处理每条消息,则需要维护自己的队列.而且您可能需要限制添加到队列的线程.

你的睡眠(1)会踩油门,但是会非常粗暴.也许它会扼杀太多,也许还不够.一般来说,您需要更精确地了解节流.通常,您可以通过跟踪队列的大小来自适应地进行限制.如果你可以避免节流这样做.它很复杂,难以很好地实现,并且会损害性能.

如果有另一个线程准备运行,则调用Sleep(0)将产生.否则Sleep(0)无效.从文档中

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run,the function returns immediately,and the thread continues execution.

另一方面,如果你需要做的只是在GUI中报告状态,那么你应该完全避免一个队列.不要将消息从线程发布到主线程.只需在主线程中运行GUI更新计时器,让主线程询问工作人员当前状态.

将该想法应用于您的代码会产生以下结果:

const
  TH_MESSAGE = WM_USER + 1; // Thread message
  TH_ParaM_FINISH = 2;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    procedure ThreadMessage(var Message: TMessage); message TH_MESSAGE;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  Count: Integer;

function ThreadProc(Parameter: Pointer): Integer;
var
  ReceiverWnd: HWND;
  I: Integer;
begin
  Result := 0;
  ReceiverWnd := Form1.Handle;
  for I := 1 to high(Integer) do
  begin
    Count := I;
  end;
  PostMessage(ReceiverWnd,GetCurrentThreadID);
end;

procedure TForm1.ThreadMessage(var Message: TMessage);
begin
  case Message.WParam of
  TH_ParaM_FINISH:
    begin
      Button1.Enabled := True;
      Timer1.Enabled := False;
    end;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption := 'Action' + IntToStr(Count);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadId: LongWord;
  ThreadHandle: THandle;
begin
  Count := -1;
  Button1.Enabled := False;
  ThreadHandle := BeginThread(nil,ThreadId);
  CloseHandle(ThreadHandle);
  Timer1.Enabled := True;
end;

多线程 – 阻止/删除线程向主UI线程发布消息的更多相关文章

  1. HTML5实现直播间评论滚动效果的代码

    这篇文章主要介绍了HTML5实现直播间评论滚动效果的代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. 前端监听websocket消息并实时弹出(实例代码)

    这篇文章主要介绍了前端监听websocket消息并实时弹出,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. HTML5之消息通知的使用(Web Notification)

    通知可以说是web中比较常见且重要的功能,私信、在线提问、或者一些在线即时通讯工具我们总是希望第一时间知道对方有了新的反馈。本篇文章主要介绍了HTML5之消息通知的使用(Web Notification),感兴趣的小伙伴们可以参考一下

  4. HTML5中的Web Notification桌面通知功能的实现方法

    这篇文章主要介绍了HTML5中的Web Notification桌面通知功能的实现方法,需要的朋友可以参考下

  5. HTML5仿微信聊天界面、微信朋友圈实例代码

    小编最近开发一个基于html5开发的一个微信聊天前端界面,功能很全面,下面小编给大家分享实例代码,需要的朋友参考下

  6. HTML5的postMessage的使用手册

    HTML5提出了一个新的用来跨域传值的方法,即postMessage,这篇文章主要介绍了HTML5的postMessage的使用手册的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. ios – Testflight无法安装应用程序

    我有几个测试人员注册了testflight并连接了他们的设备……他们有不同的ios型号……但是所有这些都有同样的问题.当他们从“safari”或“testflight”应用程序本身单击应用程序的安装按钮时……达到约90%并出现错误消息…

  8. xcode找不到匹配的配置文件

    我有一个AdhociOS应用程序,它给了我“在xcode6中找不到匹配的配置文件”,我创建了一个Adhoc配置文件,下载它,双击它并在General–Identity下选择了一个团队.但我接着得到了那条消息,并尝试使用“修复问题”按钮没有帮助.在构建设置–供应配置文件–发布我有“自动”.任何人都可以帮助我,我完全迷失了……

  9. ios – Reactive Cocoa – 以编程方式设置文本时不会调用UITextView的rac_textSignal

    我正在实现一个聊天UI,并使用ReactiveCocoa根据用户的类型调整聊天气泡的大小.目前,我正在基于textview的rac_textSignal更新UI的布局.一切都很好–除了一点:当用户发送消息时,我以编程方式清除文本字段:…我是否需要拥有一个持有currentTypedString的Nsstring,并在该字符串更新时驱动UI更改?

  10. ios – 当我关闭应用程序时,我从调试器获得消息:由于信号15而终止

    我怎么能解决这个问题,我不知道这个链接MypreviousproblemaboutCoredata对我的问题有影响吗?当我cmd应用程序的Q时,将出现此消息.Messagefromdebugger:Terminatedduetosignal15如果谁知道我以前的问题的解决方案,请告诉我.解决方法>来自调试器的消息:每当用户通过CMD-Q(退出)或STOP手动终止应用程序(无论是在iOS模拟器中还是

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部