2017-07-26 60 views
0

背景线程在我的Windows窗体应用程序仍被冻结我的UI

目前工作的一个Windows窗体应用程序,我要求创建。我遇到了一个在调用资源密集型进程时UI冻结的问题。我目前正在使用线程,据我所知,该线程用于防止UI冻结并接管整个PC。

问题

目前,当我使用的线程中调用我的基类中的方法是打开位于远程服务器上的文件。这种方法有大约30到45秒的延迟。我正在创建我的后台线程并调用它来启动。当被调用时启动,如果触发,但是当它启动时,不会等待我的线程完成,基本上给我一个空的异常。所以经过一番挖掘,我发现为了等待线程完成,你必须调用.Join()。但是,当Join被调用时,它完全冻结了我的UI。所以我的创造力试图创造一个工作周围,并创建一个while循环,直到线程不再活着并继续。但是,这也会冻结用户界面。所以我错过了什么?未提及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 
+1

是的,加入(立即在开始之后)否定了整个线程设置。你期望什么?它也会使任何Control.Invoke()调用发生死锁。 –

+1

这里的简单答案是使用BackgroundWorker –

+0

所以你知道,如果你阻止你的UI线程并强制它在你的后台工作完成之前什么都不做,你的UI将被冻结,而当你不这样做的时候,它不会。所以......你的问题是什么?你已经知道如何解决你的问题,*不要阻塞UI线程*。您已经显示了代码不要自行阻止UI线程。 – Servy

回答

3

我还要考虑这样做的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<T>的几次之一。

+0

对于阅读此答案的人来说,请注意这显示了应该使用'async void'的极少数*有效*位置之一。除非你明白'async' *非常好,并且确切地知道你在做什么,所以'async'应该返回'Task'或'Task ', –

+0

从我读的东西中,我采取了不同的方法,我在基类中的函数是一个异步任务。按照你的方式做这件事有什么好处吗?我会更新我的代码示例。 – EasyE

+0

公平点@BradleyUffner,我同意,通常你应该照你说的去做。我会编辑并提及。 –