在后台运行方法和UI线程WPF

时间:2015-12-10 08:37:29

标签: c# wpf multithreading task-parallel-library task

我在以下示例中遇到了麻烦:

public void Method()
{
  LongRunningMethod();
}

LongRunningMethod()大约需要5秒钟才能调用。我从UI线程调用Method(),所以它显然应该冻结UI。解决方法是在新Method()内运行Task,这样我就像这样运行:

Task.Factory.StartNew(()=>{Method()})

它仍然阻止了UI,所以我想是LongRunningMethod()是否正在使用UI上下文。然后我尝试了另一种解决方案:

new Thread(()=>Method()).Start()

它开始工作了。怎么可能?我知道Task不能保证在不同的线程上运行,但CLR应该足够聪明,以确定它是一个长期运行的方法。

3 个答案:

答案 0 :(得分:2)

您正在安排使用您正在使用的用户界面(UI)线程的工作     此代码中的TaskScheduler.FromCurrentSynchronizationContext())

Task nextTask = task.ContinueWith(x =>
   {
       DoSomething();
   }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

       task.Start();
     }

这就是为什么你的UI被冻结的原因。要防止尝试将TaskScheduler更改为Default

Task task = Task.Run(() => {; });
Task nextTask = task.ContinueWith(x =>
{
   //DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);

Task.Factory.StartNew is dangerous因为它使用TaskScheduler.Current而不是TaskScheduler.Default。要防止这种情况使用始终指向Task.Run的{​​{1}}。 TaskScheduler.Default是.NET 4.5中的新增功能,如果您使用的是.NET 4.0,则可以使用默认参数创建Task.Run

As MSDN says:

  

TaskFactory表示时间表   用户界面(UI)控制所在的同一线程上的任务   创建于。

<强>更新

运行方法RunTask()时会发生什么:

  1. TaskScheduler.FromCurrentSynchronizationContext())

    创建一个&#34;任务&#34;。 (任务没有运行。&#34;任务&#34;刚刚排到ThreadPool。)

  2. var task = new Task(action, cancellationTokenSource.Token);

  3. 创建一个&#34; nextTask&#34;这将开始执行AFTER&#34;任务&#34;完成后,&#34; nextTask&#34;将在您设置功能时在UI线程上执行 Task nextTask = task.ContinueWith(x => { DoSomething(); }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

    1. TaskScheduler.FromCurrentSynchronizationContext()
    2. 你执行&#34;任务&#34;。当&#34;任务&#34;完成,然后&#34; nextTask&#34;由方法&#34; task.ContinuuWith()&#34;运行这将在您编写的task.Start();

      的UI线程上执行

      总而言之,这两个任务是相互关联的,并且在UI线程上执行了(TaskScheduler.FromCurrentSynchronizationContext()的延续,这是冻结UI的一个原因。要防止出现此行为,请使用task

答案 1 :(得分:1)

这正是它的样子:

 public void RunTask(Action action){
   var task = new Task(action, cancellationTokenSource.Token);
 Task nextTask = task.ContinueWith(x =>
   {
       DoSomething();
   }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());

       task.Start();
     }

public void DoSomething()
{
    if(condition) // condition is true in this case (it's recurency but not permanent)
    RunTask(() => Method()); // method is being passed which blocks UI when invoked in RunTask method
}

public void Method()
{
  LongRunningMethod();
}

这是起点调用(UI线程):

  RunTask(()=>Action());

答案 2 :(得分:0)

只有一个猜测:Thread.Start创建一个前台线程。也许该方法在检测到它时从后台线程运行时切换到已知的前台线程。

希望它有所帮助。