首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >未在UI线程上运行的委托方法

未在UI线程上运行的委托方法
EN

Stack Overflow用户
提问于 2021-05-02 20:29:20
回答 1查看 369关注 0票数 1

我正在构建一个基于Windows窗体的应用程序,它监听端口,当它接收到特定的命令时,它会打开一个请求输入的窗口(表单)。

我的问题是,即使我使用委托的方法打开窗口,但只绘制了窗口家具/边框。表单的内容根本不呈现。

在搜索S.O.的其他答案时,似乎有两个原因:

  1. InitializeComponent()不被称为
  2. 试图从非UI线程

打开窗口。

看来2号是我的问题。当我比较表单构造函数中的ManagedThreadId和回调委托时,它们是不同的。

从文档中可以看出,委托应该确保回调在UI线程上运行。,有人能告诉我为什么不是吗?

下面是我的代码的简化版本。

表单

代码语言:javascript
复制
public wPrompt(bool silent, bool listen)
{
   InitializeComponent();
   // Thread.CurrentThread.ManagedThreadId returns 1

   // Register a handler for scan requests received via the network
   SocketListener.OpenFormRequest += OpenFormCommandHandler;
   // Class that contains the code to open a socket and listen for commands
   SocketListener.StartListening();
}

private void wPrompt_Load(object sender, System.EventArgs e)
{
   // Thread.CurrentThread.ManagedThreadId returns 3
}

// The callback that gets called by the delegate
private void OpenFormCommandHandler()
{
   // Thread.CurrentThread.ManagedThreadId returns 3

   // Open the form
   Visible = true;
}

SocketListener类

代码语言:javascript
复制
class SocketListener
{
   public delegate void OpenFormRequestEventHandler();
   public static event OpenFormRequestEventHandler OpenFormRequest;

   public static void  StartListening()
   {
      // Thread.CurrentThread.ManagedThreadId returns 1

      // Initialise and start worker thread
      workerThread = new Thread(new ThreadStart(ListenThread));
      workerThread.Start();
   }

   // A slightly modified version of the Synchronous Server Socket Example at
   // https://learn.microsoft.com/en-us/dotnet/framework/network-programming/synchronous-server-socket-example
   private static void  ListenThread()
   {
      // Thread.CurrentThread.ManagedThreadId returns 3

      // Opens socket and listens for command
      if (/* command received */) {
         OpenFormRequest?.Invoke();
      }
   }
}

委托回调函数运行在与套接字侦听器相同的线程上。不是预期的UI线程。

有人能解释一下发生了什么吗?我不做太多的.net开发,所以我很难解决这个问题。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-02 22:31:02

就我从文档中可以看出的

而言,委托应该确保回调在UI线程上运行。有人能告诉我为什么不是吗?

没有任何文档可以说明这一点。也就是说,仅仅使用委托是不够的。您必须使用一种将调用移动到UI线程上的机制来调用委托,并且在上面发布的代码中没有类似的内容。不管你读到什么,你似乎都误解了。

代码的问题是,您似乎混淆了委托的编译器生成的Invoke()方法和框架提供的Control.Invoke()方法。您的代码调用前者,而您应该调用后者。前者所做的只是实际调用委托;后者是处理将委托的执行封送到UI线程上以便在那里执行的方法。

坦率地说,与套接字相关的代码试图解决这个问题是错误的。在ListenThread()方法中,只需正常地引发事件(具有讽刺意味的是,这是您正在使用的语法,因此实际上您不需要在那里更改任何内容)。在您的OpenFormCommandHandler()方法中,您应该调用Control.Invoke()方法来执行需要在那里执行的任何代码,例如创建和显示一个新表单。

根据您最近的编辑,理论上您将如何更改事件处理程序:

代码语言:javascript
复制
private void OpenFormCommandHandler()
{
   // Thread.CurrentThread.ManagedThreadId returns 3

   // Open the form
   this.Invoke((MethodInvoker)(() => Visible = true;));
}

但是我从您的问题描述中推断,表单实际上还没有显示一次,这意味着它还没有绑定到主线程,因此Control.Invoke()不太可能工作(您可能会得到一个异常,报告窗口句柄尚未创建…)。我忘记了确切的措辞,这对我来说还不够重要,我现在就去查。

假设是这样的话,您需要从其他地方获得同步上下文。不幸的是,这个问题仍然缺乏能够给出一个更明确的答案的具体细节,从而准确地说明如何做到这一点。但取决于您的程序中还发生了什么,您可以:

  1. 将一个不同的Form实例传递给wPrompt构造函数,并在调用Invoke()时使用该实例。或者,
  2. SynchronizationContext.Current传递给wPrompt构造函数,并调用对象的Send()Post()方法(等效于Control.Invoke()Control.BeginInvoke()Post())

您还可以使用其他机制来捕获并使用同步上下文,但根据您问题中的详细信息,这两种机制中的一种会比您更好。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67360836

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档