c# - 从另一个线程启动时,我的表单无法正确显示

标签 c# winforms multithreading

情况是这样的: 我正在开发一个具有以下结构的简单应用程序:

  • FormMain(启动点)
  • 表单通知
  • CompleFunctions

对吧?

那么,在 FormMain 中我有以下函数:

private void DoItInNewThread(ParameterizedThreadStart pParameterizedThreadStart, object pParameters, ThreadPriority pThreadPriority)
{
    Thread oThread = new Thread(pParameterizedThreadStart);
    oThread.CurrentUICulture = Settings.Instance.Language;
    oThread.IsBackground = true;
    oThread.Priority = pThreadPriority;
    oThread.Name = "μRemote: Background operation";
    oThread.Start(pParameters);
}

因此,每次我需要调用位于 ComplexFunctions 上的耗时方法时,我都会执行以下操作:

// This is FormMain.cs
string strSomeParameter = "lala";
DoItInNewThread(new ParameterizedThreadStart(ComplexFunctions.DoSomething), strSomeParameter, ThreadPriority.Normal);

另一个类,FormNotification,它是一个向用户显示流程的一些信息的表单。 可以从 FormMain 或 ComplexFunctions 调用此 FormNotification。 示例:

// This is ComplexFunctions.cs
public void DoSomething(string pSomeParameter)
{
    // Imagine some time consuming task
    FormNotification formNotif = new FormNotification();
    formNotif.Notify();
}

FormNotify 有一个计时器,因此在 10 秒后关闭表单。我没有使用 formNotif.ShowDialog 因为我不想把焦点放在这个表单上。 你可以查看 this link看看我在 Notify 中做了什么。

好的,问题来了: 当我从 ComplexFunction 调用 FormNotify 时,它是从 FormMain 中的另一个线程调用的......这个 FormNotify 在一个几毫秒。 当你做这样的事情时,效果是一样的:

using(FormSomething formSomething = new FormSomething)
{
   formSomething.Show();
}

如何避免这种情况?

这些是我不想使用的可能的解决方案:

  • 在 FormNotify 中使用 Thread.Sleep(10000)
  • 使用 FormNotif.ShowDialog()

这是一个简化的场景(FormNotify 做了一些其他花哨的东西,只停留 10 秒,但它们与问题无关)。

感谢您的宝贵时间!!! 对不起,我的英语不好。

最佳答案

几乎每个 GUI 库都被设计为只允许在为此目的指定的单个线程(称为 UI 线程)中进行更改 GUI 的调用。如果您在另一个线程中,则需要安排调用以在 UI 线程中进行更改 GUI。在 .NET 中,执行此操作的方法是调用 Invoke(同步)或 BeginInvoke(异步)。等效的 Java Swing 调用是 invokeLater() —— 几乎每个 GUI 库中都有类似的函数。

有一种叫做线程亲和性的东西。 WinForm 应用程序中有两个线程,一个用于渲染,一个用于管理用户界面。您只处理用户界面线程。渲染线程保持隐藏状态——在后台运行。在 UI 线程上创建的唯一对象可以操作 UI - 即对象与 UI 线程具有线程关联性。

因为,您正在尝试从与 UI 线程不同的线程更新 UI(显示通知)。所以在你的工作线程中定义一个委托(delegate)并让 FormMain 监听这个事件。在事件处理程序中(在 FormMain 中定义)编写代码以显示 FormNotify。

当您想要显示通知时,从工作线程触发事件。

当控件的创建线程以外的线程试图访问该控件的方法或属性之一时,通常会导致不可预知的结果。常见的无效线程事件是对访问控件的 Handle 属性的错误线程的调用。将 CheckForIllegalCrossThreadCalls 设置为 true 以在调试时更轻松地查找和诊断此线程事件。请注意,当应用程序在调试器之外启动时,非法的跨线程调用将始终引发异常。

注意:将 CheckForIllegalCrossThreadCalls 设置为 ture 应该只在 DEBUGGIN SITUATIONS ONLY 中完成。不可预知的结果会发生,您最终会尝试追查很难找到的错误。

关于c# - 从另一个线程启动时,我的表单无法正确显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/160555/

相关文章:

c# - 如何确定 C# 中是否存在子目录?

c# - 在单元测试中配置 log4net 以登录到控制台并显示正确的日期和时间

c++ - 具有非 void 函数的多线程

c# - 如何以编程方式更改 Windows 10 任务栏图标大小

c# - 在 Azure Function 中找不到 EF 的 Getter 方法

c# - 以编程方式中断事件的屏幕保护程序?

c# - 来自后台 worker 的文本框文本?

c# - 在 FullRowSelect 模式下将 datagridview 单元格内容复制到剪贴板

java - 为什么并发练习书中的SafePoint类标记为@ThreadSafe?

python - 新线程阻塞主线程