WPF Dispatcher.Invoke和Dispatcher.BeginInvoke的区别

本章讲述:WPF Dispatcher.Invoke和Dispatcher.BeginInvoke的区别。

该类型的 CollectionView 不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改。

Task任务中或者线程中可用App.Current.Dispatcher.Invoke(() =>{});(等待主线程调用)更新界面上,不允许线程调度的显示或者操作 ;
在 WPF中,只有创建 DispatcherObject 的线程才能访问该对象。 例如,从主 UI 线程旋转的后台线程无法更新在 UI 线程上创建的 Button 的内容。 为了让后台线程访问 Button的 Content 属性,后台线程必须将工作委托给与 UI 线程关联的Dispatcher。 这是通过使用 Invoke 或 BeginInvoke来实现的。

Invoke 同步,BeginInvoke 是异步的。 操作将添加到指定 DispatcherPriorityDispatcher 的事件队列。
Invoke 是一种同步操作;因此,在回调返回后,控件才会返回到调用对象。
System.Windows.Application.Current.Dispatcher.BeginInvoke((System.Action)(() =>{})); 异步调用

BeginInvoke 是异步的;因此,控件在调用后立即返回到调用对象。

下面以一个简单的示例进行说明:

    界面UI布局实现:

<Window x:Class="ScreenDisplayChangeTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:diag="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        Title="MainWindow" Height="150" Width="225">
    <Grid>
        <Button x:Name="btnTest" Content="控件内容更新测试" Width="150" Height="40" Click="btnTest_Click"/>
    </Grid>
</Window>

    后台逻辑实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ScreenDisplayChangeTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void btnTest_Click(object sender, RoutedEventArgs e)
        {
            this.btnTest.Content = "主线程直接更新";
            //线程中直接更新
            Task.Run(() =>
            {
                this.btnTest.Content = "Task中更新";
            });
            //Invoke
            Task.Run(() =>
            {
                App.Current.Dispatcher.Invoke(() =>
                {
                    this.btnTest.Content = "使用Invoke 更新";
                });
            });
            //BeginInvoke 更新
            Task.Run(() =>
            {
                App.Current.Dispatcher.BeginInvoke((System.Action)(() =>
                {
                    this.btnTest.Content = "使用BeginInvoke更新";
                }));
            });
        }
    }
}

按下“F5”调试代码,会报出如下图异常信息:“调用线程无法访问此对象,因为另一个线程拥有该对象”;诸如此问题,可采用异步更新或者Invoke更新。

### WPFDispatcher 的基本概念 在 Windows Presentation Foundation (WPF) 应用程序中,`Dispatcher` 是用于管理线程间通信的核心组件之一。其主要职责是在特定的线程上下文中调度执行委托。对于大多数情况而言,这通常意味着将来自其他线程的操作转移到拥有目标控件所有权的那个唯一主线程——即UI线程上去执行。 #### `Dispatcher.Invoke` `Dispatcher.BeginInvoke` 当需要确保某些代码片段仅能在创建它的那个特殊线程里被执行时(比如修改界面元素),可以利用这两个方法: - **`Dispatcher.Invoke(Action)`**: 同步方式调用指定的方法或匿名函数,在当前调用返回前会一直阻塞直到被提交的任务完成为止[^1]。 ```csharp this.Dispatcher.Invoke(() => { MyLabel.Content = "Updated synchronously"; }); ``` - **`Dispatcher.BeginInvoke(Action)`**: 异步方式进行同样的事情;一旦安排好任务就立即让出控制权给调用者而不必等待实际处理结束. ```csharp this.Dispatcher.BeginInvoke(new Action(() => { MyLabel.Content = "Updated asynchronously"; })); ``` #### 新 UI 线程上的元素 为了安全地从非 UI 线程新界面上的对象属性值或其他资源,应当通过上述提到的方式来进行跨线程访问[^4]: ```csharp private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // 假设这是在一个后台工作者线程... // 安全地设置标签的内容 Application.Current.Dispatcher.Invoke(() => { var myLabel = FindName("MyLabel") as Label; if(myLabel != null){ myLabel.Content = "Data processed!"; } }); } ``` #### 防止死锁的发生 需要注意的是不当使用 `Dispatcher.Invoke()` 可能会引起应用程序进入无法响应的状态—也就是所谓的“死锁”。例如,如果一个正在运行的工作线程试图同步地请求回到主窗口的消息循环去改变某个可视部件的同时,后者又恰好处于挂起状态等着前者发出信号,则两者都会陷入无限期停滞不前的局面[^5]. 为了避免这种情况发生,建议尽可能采用异步模式(`BeginInvoke`)除非确实有必要立刻得到反馈结果。另外也可以考虑重构逻辑结构使得各部分之间加松耦合从而减少相互依赖程度高的场景出现几率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值