为什么内部任务不执行

本文关键字:执行 任务 内部 为什么 | 更新日期: 2023-09-27 18:23:35

在我的wpf应用程序中,我知道我应该更新主线程上的UI元素。我所做的是使用主窗口调度器来做到这一点。我只是好奇为什么这个代码不起作用:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += MainWindow_Loaded;            
    }
    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            Console.Write("test");
            Task.Factory.StartNew(() =>
            {
                // why does this code does not execute!! ???
                Thread.Sleep(1000);
                txt.Text = "Testing";
            }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
        });                      
    }
}

为什么内部任务不执行?换句话说,我的程序从未到达Thread.Sleep(1000);行。为什么是这样?

为什么内部任务不执行

当调用线程上没有同步上下文(即SynchronizationContext)时,TaskScheduler.FromCurrentSynchronizaContext()可能会抛出InvalidOperationException。Current返回null。

因此,为了捕捉UI TaskScheduler,您应该更早地获得它:

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() =>
    {
        Console.Write("test");
        Task.Factory.StartNew(() =>
        {
            Thread.Sleep(1000);
            txt.Text = "Testing";
        }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    });                      
}

您还可以使用延续任务,并在您需要的线程上运行它:

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() =>
    {
        Console.Write("test");
    }).ContinueWith(() =>
        {
            Thread.Sleep(1000);
            txt.Text = "Testing";
        }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}

您可以直接使用调度器:

public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      this.Loaded += MainWindow_Loaded;
    }
    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
      Task.Factory.StartNew(() =>
      {
        Console.Write("test");
        Thread.Sleep(1000);
        Application.Current.Dispatcher.BeginInvoke(
          DispatcherPriority.Normal, new Action(() => 
          {
            txt.Text = "Testing";
          }));
      });
    }
  }