我在以下示例中遇到了麻烦:
public void Method()
{
LongRunningMethod();
}
LongRunningMethod()
大约需要5秒钟才能调用。我从UI线程调用Method()
,所以它显然应该冻结UI。解决方法是在新Method()
内运行Task
,这样我就像这样运行:
Task.Factory.StartNew(()=>{Method()})
它仍然阻止了UI,所以我想是LongRunningMethod()
是否正在使用UI上下文。然后我尝试了另一种解决方案:
new Thread(()=>Method()).Start()
它开始工作了。怎么可能?我知道Task
不能保证在不同的线程上运行,但CLR
应该足够聪明,以确定它是一个长期运行的方法。
答案 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
。
TaskFactory
表示时间表 用户界面(UI)控制所在的同一线程上的任务 创建于。
<强>更新强>
运行方法RunTask()时会发生什么:
TaskScheduler.FromCurrentSynchronizationContext())
创建一个&#34;任务&#34;。 (任务没有运行。&#34;任务&#34;刚刚排到ThreadPool。)
var task = new Task(action, cancellationTokenSource.Token);
创建一个&#34; nextTask&#34;这将开始执行AFTER&#34;任务&#34;完成后,&#34; nextTask&#34;将在您设置功能时在UI线程上执行
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
。
TaskScheduler.FromCurrentSynchronizationContext()
你执行&#34;任务&#34;。当&#34;任务&#34;完成,然后&#34; nextTask&#34;由方法&#34; task.ContinuuWith()&#34;运行这将在您编写的task.Start();
总而言之,这两个任务是相互关联的,并且在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
创建一个前台线程。也许该方法在检测到它时从后台线程运行时切换到已知的前台线程。
希望它有所帮助。