Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 后台运行方法和UI线程WPF_C#_Wpf_Multithreading_Task Parallel Library_Task - Fatal编程技术网

C# 后台运行方法和UI线程WPF

C# 后台运行方法和UI线程WPF,c#,wpf,multithreading,task-parallel-library,task,C#,Wpf,Multithreading,Task Parallel Library,Task,我在以下示例中遇到问题: public void Method() { LongRunningMethod(); } LongRunningMethod()调用大约需要5秒钟。我正在从UI线程调用Method(),因此它显然应该冻结UI。解决方案是在一个新的任务中运行Method(),因此我是这样运行它的: Task.Factory.StartNew(()=>{Method()}) 它仍在阻塞UI,因此我认为LongRunningMethod()可能正在使用UI上下文。然后我尝试了

我在以下示例中遇到问题:

public void Method()
{
  LongRunningMethod();
}
LongRunningMethod()
调用大约需要5秒钟。我正在从UI线程调用
Method()
,因此它显然应该冻结UI。解决方案是在一个新的
任务中运行
Method()
,因此我是这样运行它的:

Task.Factory.StartNew(()=>{Method()})
它仍在阻塞UI,因此我认为
LongRunningMethod()
可能正在使用UI上下文。然后我尝试了另一种解决方案:

新线程(()=>Method()).Start()


它开始工作了。这怎么可能?我知道
任务
不能保证在不同的线程上运行,但是
CLR
应该足够聪明,能够理解它是一种长期运行的方法。

只是猜测:
线程。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线程):


您正在计划用户界面(UI)线程上的工作,因为您正在使用
TaskScheduler.FromCurrentSynchronizationContext())
在此代码中:

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

       task.Start();
     }
这就是为什么你的用户界面被冻结的原因。要防止此错误,请尝试将
任务计划程序
更改为
默认值

Task task = Task.Run(() => {; });
Task nextTask = task.ContinueWith(x =>
{
   //DoSomething();
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
因为它使用的是
TaskScheduler.Current
,而不是
TaskScheduler.Default
。要防止出现这种情况,请使用
Task.Run
,它始终指向
TaskScheduler.Default
Task.Run
在.NET 4.5中是新的,如果您在.NET 4.0中,您可以使用默认参数创建
TaskFactory

TaskScheduler.FromCurrentSynchronizationContext())
表示调度 与用户界面(UI)控件位于同一线程上的任务 创建于

更新:

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

  • var task=新任务(action,cancellationTokenSource.Token)

    创建一个“任务”。(任务未运行。“任务”仅与线程池关联。)

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

  • 创建一个“nextTask”,它将在“task”完成后开始执行,“nextTask”将在UI线程上执行,因为您已经设置了一个特性
    TaskScheduler.FromCurrentSynchronizationContext()

  • task.Start()
  • 你运行你的“任务”。当“任务”完成后,将通过“task.continuWith()”方法运行“nextTask”,该方法将在您编写的UI线程上执行
    (TaskScheduler.FromCurrentSynchronizationContext()


    总之,您的两个任务是相互关联的,
    task
    的继续是在UI线程上执行的,这是冻结UI的原因。要防止这种行为,请使用
    TaskScheduler。默认值

    您是否尝试过task.Run(()=>{Method})?它仍然冻结UI吗?LongRunningMethod更新UI吗?LongRunningMethod()中是否存在可观察到的集合?阅读关于Task.Run vs Task.Factory.New()的文章不,它不会更新UI-至少它不应该更新,它是一个外部库。它是你的外部库吗?你看到代码了吗?CLR不能使用UI线程,因为dispatcher会将任务从线程池中释放出来。不,它不是我的。我认为问题在于,该方法是从另一个使用任务调度器UI的任务运行的,所以它是继承的调度器,对吗?我不这么认为,这是不可能的。这将是真正的错误。请阅读MSDN所说的内容,因为工作被放在这个共享队列上,并最终被解队列到下一个可用的线程上。是的,但如果您注意到我不是在nextTask内调用LongRunningMethod,而是在任务内调用LongRunningMethod,该任务不是使用UI创建的任务调度器,但任务构造函数。这是流程:任务->下一个任务(UI调度器)->在下一个任务内创建的任务(称为长任务)。由于LongTask是在计划在UI线程上运行的nextTask中创建的,这意味着它将使用相同的计划程序-在本例中为UI计划程序?是否正确?@Zbigniew是,但
    Task nextTask=Task.ContinueWith(x=>
    不是创建新的
    任务
    。它只是创建新的
    任务
    变量类型。要创建新的
    任务
    或任何其他对象,C使用关键字
    新建
    。是的,但你看不到我的观点。如果你注意到对RunTask的重复调用,我试图解释一下。读完这篇文章后:eve事情很清楚。谢谢,我们正在使用UI调度程序执行下一个任务,如果条件为真,那么我们将再次运行RunTask,因此使用UI调度程序创建新任务(操作,cancellationTokenSource.Token),因为我假设Task.Factory.StartNew和new Task(…)使用当前计划程序,在这种情况下是UI。因此,如果我们不想有这种行为,我们需要将新任务(…)更改为Task.Run,默认情况下使用默认计划程序。
    Task task = Task.Run(() => {; });
    Task nextTask = task.ContinueWith(x =>
    {
       //DoSomething();
    }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);