共用方式為


在多執行緒環境中使用 Windows 執行階段物件

本文討論 .NET Framework 如何處理從 C# 和 Visual Basic 程式碼呼叫由 Windows 執行階段或 Windows 執行階段元件提供的物件的方式。

在 .NET Framework 中,您可以預設從多個線程存取任何物件,而不需要特殊處理。 您只需要對象的參考。 在 Windows 執行時間中,這類物件稱為 agile。 大部分的 Windows 執行時間類別都是敏捷式的,但少數類別則不是,甚至敏捷式類別可能需要特殊處理。

在可能的情況下,Common Language Runtime(CLR)會將來自其他來源的物件,例如 Windows 執行階段,當作 .NET Framework 物件來處理。

下列各節說明此行為對各種來源物件的影響。

以 C# 或 Visual Basic 撰寫的 Windows 執行時間元件中的物件

元件中可啟動的所有類型預設都是敏捷式。

備註

靈活度並不代表線程安全性。 在 Windows 運行時間和 .NET Framework 中,大部分類別都不是安全線程,因為線程安全性具有效能成本,而且大部分的對象永遠不會由多個線程存取。 僅在必要時同步處理個別物件的存取權(或使用執行緒安全的類別),以提高效率。

當您撰寫 Windows 執行階段元件時,您可以覆寫其預設值。 請參閱 ICustomQueryInterface 介面和 IAgileObject 介面。

來自 Windows 執行階段的物件

Windows 運行時間中的大多數類別都是敏捷的,CLR 會將它們視為敏捷式。 這些類別的文件會在類別屬性列表中列出「MarshalingBehaviorAttribute(Agile)」。 不過,某些敏捷類別(例如 XAML 控件)的成員如果未在 UI 執行緒上呼叫,可能會拋出例外。 例如,下列程式代碼會嘗試使用背景線程來設定已按下按鈕的屬性。 按鈕的 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

您可以使用按鈕 發送器 屬性,或 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 Runtime 物件的存留期,是由執行緒的存留期決定。 在視窗關閉之後,請勿嘗試存取UI線程上的物件。

如果您藉由繼承 XAML 控件或撰寫一組 XAML 控件來建立自己的控件,則控件是敏捷的,因為它是 .NET Framework 物件。 不過,如果呼叫其基類或組成類別的成員,或呼叫繼承的成員,這些成員會在從UI線程以外的任何線程呼叫時擲回例外狀況。

無法封送處理的類別

未提供封送處理資訊的 Windows 執行時間類別具有 MarshalingBehaviorAttribute 屬性,MarshalingType.None。 這類程式類別的文件會列出其屬性中的「MarshalingBehaviorAttribute(None)」。

下列程式代碼會在UI線程上建立 CameraCaptureUI 物件,然後嘗試從線程集區線程設定對象的屬性。 CLR 無法封送處理呼叫,並擲回 System.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 可辨識敏捷類別,若類別不是敏捷,則會嘗試封送處理呼叫,並在類別沒有封送處理資訊時擲回 System.InvalidCastException 例外狀況。

對於在 UI 線程上執行的物件,當從非 UI 線程呼叫時會擲回例外狀況,您可以使用 UI 線程的 CoreDispatcher 物件來切換呼叫。

另請參閱

C# 指南

Visual Basic 指南