【问题标题】:c# wpf dispatcher.beginInvoke freezec# wpf dispatcher.beginInvoke freeze
【发布时间】:2014-05-22 12:38:32
【问题描述】:

我正在开发一个与服务器通信的应用程序。

thread  = new Thread(new ThreadStart(ClientStart));
thread.SetApartmentState(ApartmentState.STA);                
thread.Start();

private void ClientStart()
    {
        tcpClient = new TcpClient(ip,3000);
        stream = tcpClient.GetStream();
        while (true) {                
            stream.Read(...);
            PrintMessage(...);
        .....
        }
}
private void PrintMessage(LogWin w, string msg)
{
  DateTime dt = DateTime.Now;
  w.GetMessageBox().Dispatcher.BeginInvoke(DispatcherPriority.Input,new Action(()=>w.GetMessageBox()
            .AppendText(String.Format("{0:d/M/yyyy HH:mm:ss}", dt) + " : " + msg)));

}

稍后我需要将结果打印到消息框。我知道我必须使用 Dispatcher,因为我在另一个线程中工作,但即使我使用 beginInvoke 方法,我的应用程序也会冻结。

根据 Sheridan 的回答编辑

现在我得到: WindowsBase.dll 中发生了“System.InvalidOperationException”类型的未处理异常

附加信息:调用线程无法访问此对象,因为不同的线程拥有它。

Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
thread = new Thread(() => ClientStart(dispatcher));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

private void ClientStart(Dispatcher dispatcher)
    { 
....

并更改了打印方式:

 dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
       w.GetMessageBox()
            .AppendText(String.Format("{0:d/M/yyyy HH:mm:ss}", dt) + " : " + msg)));

解决了使用Application.Current.Dispatcher

【问题讨论】:

  • 我相信在 MessageBox 上打印不需要调用 UI
  • 调度程序线程就是这样 - 一个线程。它只能按顺序处理事物。如果您调用 Dispatcher.Invoke,那么您的委托将被添加到队列中并根据优先级进行处理。这意味着在处理您的委托时,Dispatcher 将无法发送消息,因此您的 UI 将被锁定。
  • stream.Read 需要多长时间才能完成?您的 while(true) 语句有一个紧密的循环,因此您可能没有提供足够的资源来允许消息泵运行。

标签: c# wpf freeze dispatcher


【解决方案1】:

始终警惕Dispatcher.CurrentDispatcher,因为这将返回一个对象,以便在当前线程(可能不是 UI)上进行调度。您想要(并且通常几乎总是想要)UI 线程调度程序。

您可以通过Application.Current.Dispatcher 得到它,它总是返回 UI 线程调度程序。

令人沮丧的是,这些调用看起来如此相似......

[如果您在主窗口中,您可以拨打Dispatcher,但您可能不是这样]

【讨论】:

    【解决方案2】:

    来自 MSDN 上的Dispatcher.BeginInvoke Method 页面:

    在 WPF 中,只有创建 DispatcherObject 的线程才能访问该对象

    因此,如果您首先从后台线程调用Dispatcher,那么它只能在该后台线程上运行。相反,请确保在 UI 线程上“初始化”Dispatcher 对象:

    Dispatcher = Dispatcher.CurrentDispatcher;
    

    来自 MSDN 上的Dispatcher Class 页面:

    如果您尝试获取当前线程的 CurrentDispatcher 并且 Dispatcher 未与该线程关联,则将创建 Dispatcher

    然后将对该 UI 线程 Dispatcher 的引用传递给您的后台线程将使您能够再次从后台线程访问该 UI 线程。

    【讨论】:

      猜你喜欢
      • 2014-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多