在多线程环境中使用 Windows 运行时对象

本文讨论 .NET Framework 处理从 C# 和 Visual Basic 代码发起的对由 Windows 运行时 或 Windows 运行时 组件提供的对象的调用的方式。

在 .NET Framework 中,默认情况下可从多个线程访问任意对象,而无需进行特殊处理。您仅需要对该对象的引用。在 Windows 运行时 中,此类对象称作“敏捷对象”。大多数 Windows 运行时 类都是敏捷类,但也存在几个非敏捷类,甚至敏捷类也可能需要特殊处理。

如果可能,公共语言运行时 (CLR) 会将其他源(如 Windows 运行时)中的对象视为 .NET Framework 对象:

以下各节介绍此行为对各种源中的对象产生的影响。

用 C# 或 Visual Basic 编写的 Windows 运行时 组件中的对象

默认情况下,该组件中所有可激活的类型都是敏捷类型。

备注

灵活性并不表示线程安全。在 Windows 运行时 和 .NET Framework 中,大多数类都不是线程安全的,因为线程安全会降低性能,并且大多数对象绝不会由多个线程访问。根据需要仅同步对单个对象的访问(或使用线程安全类)会更为有效。

在创作 Windows 运行时 组件时,可以重写默认值。请参见 ICustomQueryInterface 接口和 IAgileObject 接口。

Windows 运行时 中的对象

Windows 运行时 中的大多数类都是敏捷的,CLR 会将这些类视为敏捷类。这些类的文档列出了类特性中的“MarshalingBehaviorAttribute(Agile)”。但是,如果未在 UI 线程上调用其中一些敏捷类的成员(如 XAML 控件),则它们会引发异常。例如,以下代码尝试使用后台线程来设置已单击按钮的属性。该按钮的 Content 属性会引发异常。

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await Task.Run(() => { 
                b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await Task.Run(Sub()
                           b.Content &= "."
                       End Sub)
    End Sub

通过使用其 Dispatcher 属性或 UI 线程的上下文(如按钮所在的页面)中存在的任何对象的 Dispatcher 属性,可以安全访问该按钮。以下代码使用 CoreDispatcher 对象的 RunAsync 方法来调度在 UI 线程上进行的调用。

        private async void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Button b = (Button) sender;
            await b.Dispatcher.RunAsync(
                Windows.UI.Core.CoreDispatcherPriority.Normal, 
                () => { 
                    b.Content += "."; 
            });
        }
    Private Async Sub Button_Click_2(sender As Object, e As RoutedEventArgs)
        Dim b As Button = CType(sender, Button)
        Await b.Dispatcher.RunAsync(
            Windows.UI.Core.CoreDispatcherPriority.Normal, 
            Sub()
                b.Content &= "."
            End Sub)
    End Sub

备注

在从另一个线程调用 Dispatcher 属性时,该属性不会引发异常。

在 UI 线程上创建的 Windows 运行时 对象的生存期由线程的生存期绑定。在关闭窗口后,不要尝试在 UI 线程上访问对象。

如果您通过继承 XAML 控件或通过组合一系列 XAML 控件来创建您自己的控件,则该控件是敏捷的,因为它是一个 .NET Framework 对象。但是,如果该控件调用其基类或构成类的成员,或者,如果您调用继承的成员,则当从 UI 线程之外的任何其他线程调用这些成员时,它们将引发异常。

JJ157115.collapse_all(zh-cn,VS.120).gif不能封送的类

不提供封送处理信息的 Windows 运行时 类具有带 MarshalingType.NoneMarshalingBehaviorAttribute 特性。此类的文档列出了其特性中的“MarshalingBehaviorAttribute(None)”。

以下代码在 UI 线程上创建一个 CameraCaptureUI 对象,然后尝试从线程池线程中设置该对象的属性。CLR 无法封送调用,因此将引发 InvalidCastException 异常,并显示一条消息,指示只能在创建对象的线程上下文中使用该对象。

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Task.Run(() => { 
                ccui.PhotoSettings.AllowCropping = true; 
            });
        }
    Private ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Task.Run(Sub()
                           ccui.PhotoSettings.AllowCropping = True
                       End Sub)
    End Sub

CameraCaptureUI 的文档还列出了类属性中的“ThreadingAttribute(STA)”,因为必须在单线程上下文(如 UI 线程)中创建它。

若要从另一个线程访问 CameraCaptureUI 对象,可以为 UI 线程缓存 CoreDispatcher 对象,并稍后使用它来调度在该线程上进行的调用。或者,可以从一个 XAML 对象(如页)获取调度程序,如以下代码所示。

        Windows.Media.Capture.CameraCaptureUI ccui;

        private async void Button_Click_3(object sender, RoutedEventArgs e)
        {
            ccui = new Windows.Media.Capture.CameraCaptureUI();

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                () => {
                    ccui.PhotoSettings.AllowCropping = true;
                });
        }
    Dim ccui As Windows.Media.Capture.CameraCaptureUI

    Private Async Sub Button_Click_3(sender As Object, e As RoutedEventArgs)

        ccui = New Windows.Media.Capture.CameraCaptureUI()

        Await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                    Sub()
                                        ccui.PhotoSettings.AllowCropping = True
                                    End Sub)
    End Sub

用 C++ 编写的 Windows 运行时 组件中的对象

默认情况下,该组件中可激活的类都是敏捷类。但是,利用 C++,可以对线程模型和封送处理行为进行大量控制。如本文前面所述,CLR 可识别敏捷类,在类不是敏捷类时尝试封送调用,并在类没有封送处理信息时引发 InvalidCastException 异常。

对于在 UI 线程上运行的对象(在从 UI 线程之外的线程调用这些对象时,它们将引发异常),可以使用 UI 线程的 CoreDispatcher 对象来调度调用。

请参见

概念

Visual Basic 和 C# 语言参考