我遇到以下示例的问题:
public void Method()
{
LongRunningMethod();
}
LongRunningMethod()
调用大约需要5秒。我从UI线程调用Method()
,所以它显然应该冻结UI。解决方案是在新的Task
中运行Method()
,所以我这样运行它:
Task.Factory.StartNew(()=>{Method()})
它仍然在阻塞UI,所以我想LongRunningMethod()
是否可能在使用UI上下文。然后我尝试了另一种解决方案:
new Thread(()=>Method()).Start()
它开始工作了。这怎么可能?我知道Task
不能保证在不同的线程上运行,但CLR
应该足够聪明,能够发现它是一个长时间运行的方法。
您正在用户界面(UI)线程上安排工作,因为您正在使用此代码中的TaskScheduler.FromCurrentSynchronizationContext())
:
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
}
这就是为什么你的用户界面被冻结的原因。为防止尝试将TaskScheduler
更改为Default
:
Task task = Task.Run(() => {; });
Task nextTask = task.ContinueWith(x =>
{
//DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
Task.Factory.StartNew
是危险的,因为它使用TaskScheduler.Current
而不是TaskScheduler.Default
。为了防止这种情况,请使用始终指向TaskScheduler.Default
的Task.Run
。Task.Run
在.NET 4.5中是新的,如果您在.NET 4.0中,则可以使用默认参数创建TaskFactory
。
正如MSDN所说:
TaskScheduler.FromCurrentSynchronizationContext())
表示时间表用户界面(UI)控件所在线程上的任务创建于.
更新:
运行方法RunTask()时会发生什么:
var task = new Task(action, cancellationTokenSource.Token);
创建一个"任务"。(任务未运行。"任务"刚刚被发送到ThreadPool。)
Task nextTask = task.ContinueWith(x => { DoSomething(); }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
创建一个"nextTask",它将在"task"完成后开始执行,并且当您设置了一个功能时,"nextTask"将在UI线程上执行CCD_ 22。
task.Start();
你运行你的"任务"。当"任务"完成时,"nextTask"将通过方法"task.ContinuWith()"运行,该方法将在您编写(TaskScheduler.FromCurrentSynchronizationContext()
的UI线程上执行
总之,这两个任务是相互关联的,并且task
的延续是在UI线程上执行的,这也是冻结UI的原因。要防止这种行为,请使用TaskScheduler.Default
。
这正是它的样子:
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());
只是猜测:Thread.Start
创建了一个前台线程。当该方法检测到它是从后台线程运行时,它可能会切换到已知的前台线程。
希望它能有所帮助。