【问题标题】:Is it possible to make WinForms responsive without BackgroundWorker? [closed]是否可以在没有 BackgroundWorker 的情况下使 WinForms 响应? [关闭]
【发布时间】:2022-01-18 17:25:19
【问题描述】:

我有一个在 C# 中使用 WinForms 制作的程序,它使用了另一个开发人员提供的 API,它必须在主线程中运行(因为它使用的是 WM_Messages,但也可能有其他原因) - 所以我可以不要使用 BackgroundWorker。我的程序使用该 API 至少运行了 15 分钟。因此,当我运行它并单击表单时,它会冻结并崩溃,因为它没有响应。 在使用该 API 时,我可以做些什么来使表单响应并且不触发 Windows 警报“应用程序没有响应”?

这是我在给定文件夹中的所有文件名循环运行的代码:

fApi.ApiSetDates(DateTime.MinValue, DateTime.MinValue, invoiceIssueDate.Year, invoiceIssueDate.Month);
try
    {
        if (fApi.ImportFakturFromXML(fileName) != 0)
        {
            throw new Exception(fApi.GetLastError());
        }
        File.Delete(fileName);
    }
    catch (Exception x)
    {
        MessageBox.Show(x.ToString());
    }

【问题讨论】:

  • 邮政编码显示您实际在做什么、您正在调用什么样的函数以及这些函数的用途说明。
  • 这里没有太多背景信息,但请注意,即使您调用 API 函数仍然可以向您的主窗口发布或发送消息(我假设您在某个时候将句柄传递给它)它来自后台线程。
  • 你是说一次调用其他开发人员的代码可能需要 15 分钟?
  • @TK-421 在这种情况下,您有机会从自己的代码中获得收益。我建议设置一个间隔为 0 的计时器并在每个 Tick 上运行一次迭代。这至少可以让你每 30 秒到 1 分钟刷新一次 UI。
  • 隐藏问题的一种基本方法是隐藏窗口,当工作完成时使用 NotifyIcon 提醒用户。另一种方法是创建一个对此类库友好的工作线程,example

标签: c# winforms


【解决方案1】:

这里是一个小型 Windows 窗体的非设计人员生成的代码,当单击它的唯一按钮时,它会在一个循环中执行许多长时间运行的 UI 线程阻塞操作。与我最初在 cmets 中建议的 Timer 相比,此代码更具可读性和可维护性,并且比 D.Kastier 在 cmets 中建议的答案稍微简单一些。

namespace LongRunningLoop
{
   public partial class Form1 : Form
   {
      private bool m_Closed = false;

      public Form1()
      {
         InitializeComponent(); // set up button1
      }

      // async void event handler.  Usually async void
      // is a no-no, but here we really do want to fire and forget.
      // We prevent nasty things from happening by disabling the UI
      // before starting the main work and exiting if we detect
      // that the form has been closed.
      private async void button1_Click(object sender, EventArgs e)
      {
         // Disable the UI so there's no reentrancy during the Task.Delay()
         button1.Enabled = false;
         for (int i = 0; i < 60; i++)
         {
            if (m_Closed)
            {
               // Don't keep doing work if the user has closed the form
               break;
            }
            Thread.Sleep(5000); // A long-running, blocking call (sleep thread for 5 seconds)
            await Task.Delay(100); // Yield to allow other events to be processed
         }
         // Re-enable the UI
         button1.Enabled = true;
      }

      private void Form1_FormClosed(object sender, FormClosedEventArgs e)
      {
         // Set a flag to signal the loop to exit
         m_Closed = true;
      }
   }
}

【讨论】:

  • 所以“等待Task.Delay(100);”是什么让 UI 响应并且没有被 Windows 关闭为“应用程序没有响应”?
  • 基本上,是的。它将控制权返回给 UI 线程,说“在 100 毫秒后从我离开的地方继续。” UI 线程始终在处理消息,因此 100 毫秒给了它时间来处理它在代码中绑定时收到的任何输入或其他消息。当 Windows 注意到你的应用程序没有处理这些消息时,它会显示应用程序没有响应消息。 Task.Delay 是否会经常发生以使 Windows 确信您的应用处于活动状态,这还有待观察。
  • 我最后的评论在技术上有点误导。您看到的所有代码都在 UI 线程上执行;我所说的“将控制权返回给 UI 线程”实际上是指“将控制权返回给消息处理循环”或类似的东西,但即使这样也令人困惑,因为它从未离开过那个循环;处理您的事件是循环负责的事情之一。也许我的意思是“允许消息处理循环继续处理其他消息”。并不是说你需要知道任何这些代码才能工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-13
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 2013-12-18
  • 1970-01-01
  • 2014-06-16
相关资源
最近更新 更多