次の方法で共有


マルチスレッド環境での Windows ランタイム オブジェクトの使用

この記事では、Windows ランタイムまたは Windows ランタイム コンポーネントによって提供されるオブジェクトに対する C# および Visual Basic コードからの呼び出しを .NET Framework が処理する方法について説明します。

.NET Framework では、特別な処理を行わずに、既定で複数のスレッドから任意のオブジェクトにアクセスできます。 必要なのは、オブジェクトへの参照です。 Windows ランタイムでは、このようなオブジェクトはアジャイルと呼ばれます。 ほとんどの Windows ランタイム クラスはアジャイルですが、一部のクラスはアジャイルではなく、アジャイル クラスでも特別な処理が必要な場合があります。

可能な限り、共通言語ランタイム (CLR) は、Windows ランタイムなどの他のソースのオブジェクトを、.NET Framework オブジェクトであるかのように扱います。

  • オブジェクトが IAgileObject インターフェイスを実装している場合、または MarshalingType.Agileを持つ MarshalingBehaviorAttribute 属性を持つ場合、CLR はそれをアジャイル オブジェクトとして扱います。

  • CLR が、ターゲット オブジェクトのスレッド コンテキストに対して行われたスレッドからの呼び出しをマーシャリングできる場合は、透過的に行われます。

  • オブジェクトに MarshalingBehaviorAttribute 属性と MarshalingType.Noneがある場合、クラスはマーシャリング情報を提供しません。 CLR は呼び出しをマーシャリングできないため、InvalidCastException 例外がスローされ、オブジェクトは作成されたスレッド コンテキストでのみ使用できることを示すメッセージが表示されます。

以降のセクションでは、この動作がさまざまなソースのオブジェクトに及ぼす影響について説明します。

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

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 スレッドを除く任意のスレッドから呼び出されたときに例外をスローします。

マーシャリングできないクラス

マーシャリング情報を提供しない Windows ランタイム クラスには、MarshalingType.Noneを持つ MarshalingBehaviorAttribute 属性があります。 このようなクラスのドキュメントでは、属性の中に "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 のドキュメントでは、UI スレッドなどのシングル スレッド コンテキストで作成する必要があるため、クラスの属性の中に "ThreadingAttribute(STA)" も一覧表示されます。

別のスレッドから 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 ガイド