异步同步问题

想要实现的效果是,button等待3秒之后,button内容是www.wpfsoft.com,然后1秒之后是xxxxxxxxxxx。

 public MainWindow()
 {
     InitializeComponent();
     Task.Factory.StartNew(() =>
     {
         Task.Delay(3000).Wait();

         button.Dispatcher.Invoke(() =>
         {
             button.Content = "www.wpfsoft.com";
         });
     });
     Thread.Sleep(4000);
     button.Content = "xxxxxxxxxxx";
 }

这里有几个关键点需要注意:

异步与同步的混合使用:

Task.Delay(3000) 是一个异步操作,它不会阻塞主线程。
Thread.Sleep(4000) 是一个同步阻塞操作,它会阻塞当前线程(这里是主线程)4秒。

主线程与UI更新:

在WPF中,UI控件必须在主线程上进行更新。如果在其他线程尝试更新UI控件,则会导致异常。
button.Dispatcher.Invoke 方法用于确保更新操作在主线程上执行。
代码执行顺序:
Task.Delay(3000).Wait(); 会等待3秒然后更新按钮内容为 “www.wpfsoft.com”。
Thread.Sleep(4000); 阻塞了主线程4秒,在这期间任何在主线程上等待的操作都不会被执行。
button.Content = "xxxxxxxxxxx"; 这行代码试图在主线程上直接修改按钮内容,但是由于前面有 Thread.Sleep(4000),这个操作会在4秒后执行。
Thread.Sleep(4000) 持续的时间比 Task.Delay(3000) 更长,因此 button.Content = "xxxxxxxxxxx"; 这行代码会在 Task.Delay(3000) 完成之后才执行。

运行之后后只会看到www.wpfsoft.com(不是很能理解)

如果你想要实现预期行为,应该避免使用 Thread.Sleep 来阻塞主线程,而是使用异步方法来延迟后续操作。

Task.Factory.StartNew(() =>
{
    Task.Delay(3000).Wait();
    
    button.Dispatcher.Invoke(() =>
    {
        button.Content = "www.wpfsoft.com";
    });

    // 再次延迟1秒
    Task.Delay(1000).Wait();

    button.Dispatcher.Invoke(() =>
    {
        button.Content = "xxxxxxxxxxx";
    });
});

上述代码虽然可以工作,但在等待期间仍会阻塞当前线程。更好的做法是使用async/await来处理这些延迟操作,并确保所有的UI更新都在主线程上执行。这样可以避免阻塞主线程,并且更符合现代异步编程的最佳实践。

  1. Task.Factory.StartNew(() => { ... });
    创建一个新的后台任务,该任务将执行一个匿名方法(lambda 表达式)。StartNew 方法返回一个表示此任务的 Task 对象。
    匿名方法 { ... } 将在这个新线程上执行。
  2. Task.Delay(3000).Wait();
    Task.Delay(3000) 创建一个延迟任务,它会在3秒后完成。
    .Wait() 方法阻塞当前线程直到延迟任务完成。这里的“当前线程”是指 StartNew 创建的那个后台线程,而不是UI线程或主线程。
  3. button.Dispatcher.Invoke(() => { ... });
    Dispatcher.Invoke 方法用于在UI线程上执行一个委托(delegate),因为UI控件只能在UI线程上安全地访问和修改。
    匿名方法 () => { button.Content = "www.wpfsoft.com"; } 在UI线程上设置按钮的内容为 “www.wpfsoft.com”。
  4. Task.Delay(1000).Wait();
    同样地,创建一个延迟任务,这次延迟1秒,并等待其完成。
    再次调用 button.Dispatcher.Invoke(() => { ... });
    同第3步一样,使用 Dispatcher.Invoke 来更新按钮的内容,这次设置为 “xxxxxxxxxxx”。
    这种方法虽然有效,但它并不是最佳实践,因为它在后台线程上使用了 Wait() 方法来阻塞线程。在后台线程上阻塞可能不是大问题,但对于性能敏感的应用来说,这仍然不是一个好的做法。

使用async/await语法来编写异步代码,这样可以让代码更加清晰,并且不会阻塞任何线程。

private async void SomeMethod()
{
    await Task.Run(async () =>
    {
        await Task.Delay(3000);

        button.Dispatcher.Invoke(() => 
        {
            button.Content = "www.wpfsoft.com";
        });

        await Task.Delay(1000);

        button.Dispatcher.Invoke(() => 
        {
            button.Content = "xxxxxxxxxxx";
        });
    });
}

在这个版本中,SomeMethod 方法应该标记为 async,并且可以被其他异步方法调用。await Task.Delay(…) 会异步地等待指定的时间,不会阻塞任何线程。同样,所有对UI控件的操作都通过 Dispatcher.Invoke 在UI线程上执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值