运行 后台方法和 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.Default
。 Task.Run
是 .NET 4.5 中的新增功能,如果您使用的是 .NET 4.0,则可以使用默认参数创建 TaskFactory
。
TaskScheduler.FromCurrentSynchronizationContext())
means schedule
a task on the same thread that the user interface (UI) control was
created on.
更新:
当你 运行 方法 RunTask():
时会发生什么
var task = new Task(action, cancellationTokenSource.Token);
创建 "task"。 (任务不是 运行。"task" 只是查询到线程池。)
Task nextTask = task.ContinueWith(x =>
{
DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
创建一个 "nextTask",它将在 "task" 完成后开始执行,并且 "nextTask" 将在您设置功能时在 UI 线程上执行
TaskScheduler.FromCurrentSynchronizationContext()
.
task.Start();
你运行你的"task"。 "task" 完成后,"nextTask" 通过方法 "task.ContinuuWith()" 变为 运行,它将在您编写的 UI 线程上执行 (TaskScheduler.FromCurrentSynchronizationContext()
所以总而言之,您的两个任务是相互关联的,task
的继续是在 UI 线程上执行的,这是冻结您的 UI 的原因。要防止此行为,请使用 TaskScheduler.Default
.
我遇到以下示例的问题:
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.Default
。 Task.Run
是 .NET 4.5 中的新增功能,如果您使用的是 .NET 4.0,则可以使用默认参数创建 TaskFactory
。
TaskScheduler.FromCurrentSynchronizationContext())
means schedule a task on the same thread that the user interface (UI) control was created on.
更新:
当你 运行 方法 RunTask():
时会发生什么var task = new Task(action, cancellationTokenSource.Token);
创建 "task"。 (任务不是 运行。"task" 只是查询到线程池。)
Task nextTask = task.ContinueWith(x => { DoSomething(); }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
创建一个 "nextTask",它将在 "task" 完成后开始执行,并且 "nextTask" 将在您设置功能时在 UI 线程上执行
TaskScheduler.FromCurrentSynchronizationContext()
.
task.Start();
你运行你的"task"。 "task" 完成后,"nextTask" 通过方法 "task.ContinuuWith()" 变为 运行,它将在您编写的 UI 线程上执行 (TaskScheduler.FromCurrentSynchronizationContext()
所以总而言之,您的两个任务是相互关联的,task
的继续是在 UI 线程上执行的,这是冻结您的 UI 的原因。要防止此行为,请使用 TaskScheduler.Default
.