다중 스레드 환경에서 Windows 런타임 개체 사용

이 문서에서는 .NET Framework가 C# 및 Visual Basic 코드에서 Windows 런타임 또는 Windows 런타임 구성 요소에서 제공하는 개체로의 호출을 처리하는 방식을 설명합니다.

.NET Framework에서는 특수 처리 없이 기본적으로 여러 스레드에서 모든 개체에 액세스가 가능합니다. 개체에 대한 래퍼런스만 있으면 가능합니다. Windows 런타임에서는 이러한 개체를 Agile이라고 합니다. 대부분의 Windows 런타임 클래스는 Agile이지만 몇몇은 그렇지 않으며, Agile 클래스에도 특수한 처리가 필요할 수 있습니다.

가능한 경우 CLR(공용 언어 런타임)은 Windows 런타임 같은 다른 원본의 개체를 마치 .NET Framework 개체인 것처럼 처리합니다.

  • 개체가 IAgileObject 인터페이스를 구현하거나 MarshalingType.AgileMarshalingBehaviorAttribute 특성을 갖고 있는 경우 CLR은 이 개체를 Agile로 처리합니다.

  • CLR이 스레드에서 대상 개체의 스레딩 컨텍스트로 호출을 마샬링할 수 있는 경우 명료하게 수행됩니다.

  • 개체에 MarshalingType.NoneMarshalingBehaviorAttribute 특성이 있는 경우 클래스에서 마샬링 정보를 제공하지 않습니다. CLR은 호출을 마샬링할 수 없으며, 따라서 개체가 만들어진 스레딩 컨텍스트에만 개체를 사용할 수 있다는 내용의 InvalidCastException 예외를 throw합니다.

다음 섹션에서는 이 동작이 다양한 원본 개체에 미치는 영향에 대해 설명합니다.

C# 또는 Visual Basic으로 작성된 Windows 런타임 구성 요소의 개체

활성화할 수 있는 요소의 모든 형식은 기본적으로 agile입니다.

참고 항목

민첩성이 스레드 안전성을 의미하지는 않습니다. 스레드 안전에는 성능 저하가 수반되며 여러 스레드에서 액세스하는 개체가 별로 없기 때문에 Windows 런타임 및 .NET Framework에서 대부분 클래스는 스레드로부터 안전하게 보호되지 않습니다. 필요에 따라 개별 개체에 대한 액세스를 동기화하거나 스레드 안전 클래스를 사용하는 것이 더 효율적입니다.

Windows 런타임 구성 요소를 작성할 때 기본값을 무시할 수 있습니다. ICustomQueryInterface 인터페이스 및 IAgileObject 인터페이스를 참조하세요.

Windows 런타임의 개체

대부분의 Windows 런타임 클래스는 Agile이며, CLR은 이러한 클래스를 Agile로 처리합니다. 이러한 클래스에 대한 설명서에는 클래스 특성 중 "MarshalingBehaviorAttribute(Agile)"가 나열됩니다. 그러나 XAML 컨트롤과 같은 이러한 agile 클래스의 일부 멤버는 UI 스레드에서 호출되지 않은 경우 예외를 허용합니다. 예를 들어 다음 코드는 백그라운드 스레드를 사용하여 클릭한 버튼의 속성을 설정하려고 합니다. 단추의 콘텐츠 속성에서 예외를 throw합니다.

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속성은 다른 스레드에서 호출될 때 예외를 throw하지 않습니다.

UI 스레드에서 생성되는 Windows 런타임 개체의 수명은 스레드 수명으로 제한됩니다. 창이 닫힌 후에는 UI 스레드의 개체에 액세스하지 마십시오.

XAML 컨트롤을 물려받거나 XAML 컨트롤 집합을 작성하여 고유한 컨트롤을 만드는 경우 컨트롤은 .NET Framework 개체이므로 aglie 클래스 입니다. 그러나 기본 클래스 또는 구성 요소 클래스의 멤버를 호출하거나 물려받은 멤버를 호출하는 경우 해당 멤버는 UI 스레드를 제외한 모든 스레드에서 호출될 때 예외를 허용합니다.

마샬링할 수 없는 클래스

마샬링 정보를 제공하지 않는 Windows 런타임 클래스는 MarshalingType.NoneMarshalingBehaviorAttribute 특성을 갖습니다. 이러한 클래스에 대한 설명서에는 특성 중 "MarshalingBehaviorAttribute(None)"가 나열됩니다.

다음 코드는 UI 스레드에 CameraCaptureUI 개체를 만든 후 스레드 풀 스레드의 개체 속성을 설정하려고 시도합니다. CLR은 호출을 마샬링할 수 없으며, 따라서 개체가 만들어진 스레딩 컨텍스트에만 개체를 사용할 수 있다는 내용의 System.InvalidCastException 예외를 throw합니다.

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 런타임 구성 요소의 개체

기본적으로 활성화할 수 있는 구성 요소의 클래스는 agile입니다. 그러나 C++는 스레딩 모델 및 마샬링 동작에 대한 상당한 컨트롤을 허용합니다. 이 문서의 앞부분에서 설명했듯이, CLR은 Agile 클래스를 인식하고, 클래스가 Agile이 아니면 호출을 마샬링하려고 시도하고, 클래스에 마샬링 정보가 없으면 System.InvalidCastException 예외를 throw합니다.

UI에서 실행되고 UI 스레드가 아닌 다른 스레드에서 호출되면 예외를 throw하는 개체의 경우 UI 스레드의 CoreDispatcher 개체를 사용하여 호출을 발송할 수 있습니다.

참고 항목

C# 가이드

Visual Basic 가이드