【问题标题】:Threading in my windows form app is still freezing my UI我的 Windows 窗体应用程序中的线程仍然冻结我的 UI
【发布时间】:2017-07-26 17:33:52
【问题描述】:

背景

目前正在开发我要求创建的 Windows 窗体应用程序。当调用资源密集型进程时,我遇到了 UI 冻结的问题。我目前正在使用我理解的线程来防止 UI 冻结并接管整个电脑。

问题

目前,当我使用线程调用基类中的一个方法时,该方法是打开位于远程服务器上的文件。此方法有大约 30 到 45 秒的延迟。我正在创建我的后台线程并调用它来启动。当被调用以启动时如果触发,但是当它触发时它不会等待我的线程完成基本上给我一个空异常。因此,经过一番挖掘,我发现为了等待线程完成,您必须调用.Join()。但是,当调用 Join 时,它完全冻结了我的 UI。所以我的聪明才智试图创建一个解决方法并创建一个 while 循环,直到线程不再活动并继续。但是,这也冻结了 UI。所以我错过了什么吗? MSDN Doc中没有提到这一点

代码示例

class BaseClass
{
    public CWClient ClientFileContext(string clientFile, bool compress)
    {
        Client clientContext = null;
        try
        {
            if (compress == true)
            {
                clientContext = appInstance.Clients.Open2(clientFile, superUser, passWord, OpenFlags.ofCompressed);
            }
            else
            {
                clientContext = appInstance.Clients.Open2(clientFile, superUser, passWord, OpenFlags.ofNone);
            }
        }
        catch (Exception ex)
        {
            //TODO
        }
        return clientContext;
    }
}

  public partial class Form1 : Form
  {
   private void button1_Click(object sender, EventArgs e) 
    {
        BaseClass wpSec = new BaseClass();
        CWClient client = null;               

        Thread backgroundThread = new Thread(
            new ThreadStart(() =>
            {
                client = wpSec.ClientFileContext(selectedFileFullPath, true);
            }
        ));
        backgroundThread.Start();
        //backgroundThread.Join(); << Freezes the UI
        var whyAreYouNotWorking = "Stop";
    }
  }

我尝试过的解决方法

 while (backgroundThread.IsAlive == true)
 {
    for (int n = 0; n < 100; n++)
    {
       Thread.Sleep(500);
       progressBar1.BeginInvoke(new Action(() => progressBar1.Value = n));
    }
 }
  // This also freezes the UI

【问题讨论】:

  • 是的,加入(在开始后立即)否定了整个线程设置。你期待什么?它还会使任何 Control.Invoke() 调用死锁。
  • 这里的简单答案是使用 BackgroundWorker
  • 所以你知道如果你阻塞你的 UI 线程并强制它在后台工作完成之前什么都不做,你的 UI 就会被冻结,如果你不这样做,它就不会。所以……你有什么问题。您已经知道如何解决您的问题,不要阻塞 UI 线程。您已经展示了不自己阻塞 UI 线程的代码。
  • @Servy 正确我没有阻塞 UI,但是我仍然需要从我启动的线程加载结果。
  • @EasyE 从后台线程执行此操作,而不是在后台线程完成之前阻止 UI 线程执行任何操作。

标签: c# multithreading winforms


【解决方案1】:

我也会为此研究asyncawait 模式。在这篇文章中解释:Using async await still freezes GUI

您的代码应该与此类似(基类不变):

public partial class Form1 : Form
{
   private async void button1_Click(object sender, EventArgs e) 
   {
      BaseClass wpSec = new BaseClass();
      CWClient client = await Task.Run(() =>
          {
              return wpSec.ClientFileContext(selectedFileFullPath, true);
          }
      );
      var whyAreYouNotWorking = "Stop";
   }
}

这是粗略的东西,但希望它给出了启动任务的基本概念,然后在 async 方法中等待结果。如果你不需要你的 BaseClass,那也可以在 lambda 中,只留下你真正想要的。

上面@Chris Dunaway 的链接也非常好。 http://blog.stephencleary.com/2013/08/taskrun-vs-backgroundworker-round-3.html

编辑:正如@BradlyUffner 所提到的,这也是您应该使用async void 的少数几次之一,并且应该宁愿在几乎所有其他情况下返回TaskTask&lt;T&gt;

【讨论】:

  • 对于阅读此答案的人,请注意,这显示了应使用async void 的极少数有效 位置之一。任何其他 async 都应该返回 TaskTask&lt;T&gt;,除非你非常了解 async 非常,并且确切地知道你在做什么,
  • 从我所阅读的内容来看,我采用了不同的方法,并使我在基类中的函数成为异步任务。这样做有好处吗?我将更新我的代码示例。
  • 公平点@BradleyUffner,我同意,通常你应该照你说的做。我会编辑并提到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多