运行 后台方法和 UI 线程 WPF

Running method in the background and UI Thread WPF

我遇到以下示例的问题:

public void Method()
{
  LongRunningMethod();
}

LongRunningMethod() 调用大约需要 5 秒。我从 UI 线程调用 Method(),所以它显然应该冻结 UI。解决方案是 运行 Method() 在一个新的 Task 中,所以我 运行 这样做:

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

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

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

它开始工作了。这怎么可能?我知道 Task 不能保证在不同的线程上是 运行 但是 CLR 应该足够聪明来弄清楚它是一个很长的 运行ning 方法。

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

希望对您有所帮助。

这正是它的样子:

 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());

您正在用户界面 (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,它始终指向 TaskScheduler.DefaultTask.Run 是 .NET 4.5 中的新增功能,如果您使用的是 .NET 4.0,则可以使用默认参数创建 TaskFactory

As MSDN says:

TaskScheduler.FromCurrentSynchronizationContext()) means schedule a task on the same thread that the user interface (UI) control was created on.

更新:

当你 运行 方法 RunTask():

时会发生什么
  1. var task = new Task(action, cancellationTokenSource.Token);

    创建 "task"。 (任务不是 运行。"task" 只是查询到线程池。)

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

创建一个 "nextTask",它将在 "task" 完成后开始执行,并且 "nextTask" 将在您设置功能时在 UI 线程上执行 TaskScheduler.FromCurrentSynchronizationContext().

  1. task.Start();

你运行你的"task"。 "task" 完成后,"nextTask" 通过方法 "task.ContinuuWith()" 变为 运行,它将在您编写的 UI 线程上执行 (TaskScheduler.FromCurrentSynchronizationContext()

所以总而言之,您的两个任务是相互关联的,task 的继续是在 UI 线程上执行的,这是冻结您的 UI 的原因。要防止此行为,请使用 TaskScheduler.Default.