Threading-ModellThreading Model

Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) soll Entwicklern bei Problemen mit Threading helfen.is designed to save developers from the difficulties of threading. Als Ergebnis der meisten WPFWPF -Entwickler keine Schnittstelle schreiben, die mehr als einem Thread verwendet.As a result, the majority of WPFWPF developers won't have to write an interface that uses more than one thread. Da Multithreadprogramme komplex und schwierig zu debuggen sind, sollten sie vermieden werden, wenn Singlethread-Lösungen vorhanden sind.Because multithreaded programs are complex and difficult to debug, they should be avoided when single-threaded solutions exist.

Ganz gleich, wie gut entworfen wurde, wird keine UIUI Framework wird immer in der Lage, eine Singlethread-Lösung für jede Art von Problem bereitzustellen.No matter how well architected, however, no UIUI framework will ever be able to provide a single-threaded solution for every sort of problem. WPFWPF kommt dem nahe, aber es Situationen gibt, in denen mehrere Threads verbessern, weiterhin Benutzeroberfläche (User Interface, UI)user interface (UI) -Reaktionsfähigkeit oder die Anwendungsleistung.comes close, but there are still situations where multiple threads improve Benutzeroberfläche (User Interface, UI)user interface (UI) responsiveness or application performance. In diesem Artikel werden zunächst einige Hintergrundinformationen angegeben, dann einige dieser Situationen beschrieben und am Ende einige Details auf niedriger Ebene besprochen.After discussing some background material, this paper explores some of these situations and then concludes with a discussion of some lower-level details.

Hinweis

In diesem Thema wird erläutert, threading mithilfe der BeginInvoke Methode zum asynchronen Aufrufen.This topic discusses threading by using the BeginInvoke method for asynchronous calls. Sie können auch asynchrone Aufrufe vornehmen, durch den Aufruf der InvokeAsync -Methode, die nehmen ein Action oder Func<TResult> als Parameter.You can also make asynchronous calls by calling the InvokeAsync method, which take an Action or Func<TResult> as a parameter. Die InvokeAsync Methode gibt eine DispatcherOperation oder DispatcherOperation<TResult>, die eine Task Eigenschaft.The InvokeAsync method returns a DispatcherOperation or DispatcherOperation<TResult>, which has a Task property. Können Sie die await -Schlüsselwort entweder mit der DispatcherOperation oder mit dem zugehörigen Task.You can use the await keyword with either the DispatcherOperation or the associated Task. Wenn Sie synchron auf das Task warten müssen, das von DispatcherOperation oder DispatcherOperation<TResult> zurückgegeben wird, rufen Sie die DispatcherOperationWait-Erweiterungsmethode auf.If you need to wait synchronously for the Task that is returned by a DispatcherOperation or DispatcherOperation<TResult>, call the DispatcherOperationWait extension method. Aufrufen von Task.Wait führt zu einem Deadlock.Calling Task.Wait will result in a deadlock. Weitere Informationen zur Verwendung einer Task zum Ausführen von asynchroner Vorgängen finden Sie unter Aufgabenparallelität.For more information about using a Task to perform asynchronous operations, see Task Parallelism. Die Invoke Methode verfügt auch über Überladungen, die eine Action oder Func<TResult> als Parameter.The Invoke method also has overloads that take an Action or Func<TResult> as a parameter. Sie können die Invoke Methode zum Erstellen von synchronen Aufrufe durch Übergabe eines Delegaten Action oder Func<TResult>.You can use the Invoke method to make synchronous calls by passing in a delegate, Action or Func<TResult>.

Übersicht und VerteilerOverview and the Dispatcher

In der Regel WPFWPF Anwendung beginnt mit zwei Threads: einer für das Rendering und einer für die Verwaltung der UIUI.Typically, WPFWPF applications start with two threads: one for handling rendering and another for managing the UIUI. Das Renderingthread läuft effektiv verborgen im Hintergrund, während die UIUI Thread Eingaben empfängt, behandelt Ereignisse, den Bildschirm zeichnet und Anwendungscode ausführt.The rendering thread effectively runs hidden in the background while the UIUI thread receives input, handles events, paints the screen, and runs application code. Die meisten Anwendungen verwenden einen einzelnen UIUI thread, aber in einigen Situationen besser mehrere verwenden kann.Most applications use a single UIUI thread, although in some situations it is best to use several. Dies wird weiter unten mit einem Beispiel erläutert.We’ll discuss this with an example later.

Die UIUI Thread stellt Arbeitsaufgaben in ein Objekt mit dem Namen einer Dispatcher.The UIUI thread queues work items inside an object called a Dispatcher. Vom Dispatcher werden Arbeitsaufgaben nach Priorität ausgewählt und jeweils vollständig ausgeführt.The Dispatcher selects work items on a priority basis and runs each one to completion. Jede UIUI Thread muss über mindestens eine verfügen Dispatcher, und jede Dispatcher kann Arbeitsaufgaben in genau einem Thread ausführen.Every UIUI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread.

Der Trick zum Erstellen von reaktionsfähigen und benutzerfreundlichen Anwendungen besteht darin zu maximieren der Dispatcher Durchsatz, indem die Arbeitsaufgaben klein gehalten.The trick to building responsive, user-friendly applications is to maximize the Dispatcher throughput by keeping the work items small. Diese Elemente nicht veralten, wo der Dispatcher Warteschlange, die Verarbeitung warten.This way items never get stale sitting in the Dispatcher queue waiting for processing. Jede spürbare Verzögerung zwischen Eingabe und Antwort kann für einen Benutzer frustrierend sein.Any perceivable delay between input and response can frustrate a user.

Wie werden WPFWPF soll, dass Anwendungen große Vorgänge verarbeiten?How then are WPFWPF applications supposed to handle big operations? Was geschieht, wenn der Code eine aufwändige Berechnung beinhaltet oder eine Abfrage einer Datenbank auf einem Remoteserver ausführen muss?What if your code involves a large calculation or needs to query a database on some remote server? Die Antwort in der Regel ist, der umfangreiche Vorgang in einem eigenen Thread, wodurch die UIUI Thread frei, um auf Elemente in der Regel die Dispatcher Warteschlange.Usually, the answer is to handle the big operation in a separate thread, leaving the UIUI thread free to tend to items in the Dispatcher queue. Wenn der umfangreiche Vorgang abgeschlossen ist, können sie melden, dass das Ergebnis an die UIUI Thread für die Anzeige.When the big operation is complete, it can report its result back to the UIUI thread for display.

In der Vergangenheit WindowsWindows ermöglicht UIUI nur von einem Thread den Zugriff auf Elemente, die sie erstellt wurden.Historically, WindowsWindows allows UIUI elements to be accessed only by the thread that created them. Dies bedeutet, dass ein Hintergrundthread bei einer Aufgabe mit langer Laufzeit kein Textfeld aktualisieren kann, wenn er abgeschlossen ist.This means that a background thread in charge of some long-running task cannot update a text box when it is finished. WindowsWindows ist diese Option, um die Integrität gewährleistet UIUI Komponenten.does this to ensure the integrity of UIUI components. Ein Listenfeld könnte merkwürdig aussehen, wenn sein Inhalt während des Zeichnens von einem Hintergrundthread aktualisiert werden würde.A list box could look strange if its contents were updated by a background thread during painting.

WPFWPF verfügt über einen integrierten gegenseitigen Ausschlussmechanismus, der diese Koordination erzwingt.has a built-in mutual exclusion mechanism that enforces this coordination. Die meisten Klassen in WPFWPF abgeleitet DispatcherObject.Most classes in WPFWPF derive from DispatcherObject. Während der Erstellung einer DispatcherObject speichert einen Verweis auf die Dispatcher mit dem aktuell ausgeführten Thread verknüpft.At construction, a DispatcherObject stores a reference to the Dispatcher linked to the currently running thread. Faktisch sind die DispatcherObject ordnet den Thread, der erstellt wird.In effect, the DispatcherObject associates with the thread that creates it. Bei der programmausführung ein DispatcherObject können rufen Sie den öffentlichen VerifyAccess Methode.During program execution, a DispatcherObject can call its public VerifyAccess method. VerifyAccess überprüft die Dispatcher den aktuellen Thread zugeordnet, und vergleicht ihn mit der Dispatcher Referenz, die während der Konstruktion gespeichert.VerifyAccess examines the Dispatcher associated with the current thread and compares it to the Dispatcher reference stored during construction. Wenn sie nicht übereinstimmen, VerifyAccess löst eine Ausnahme aus.If they don’t match, VerifyAccess throws an exception. VerifyAccess am Anfang jeder Methode aufgerufen werden soll eine DispatcherObject.VerifyAccess is intended to be called at the beginning of every method belonging to a DispatcherObject.

Wenn nur ein Thread ändern, kann die UIUI, wie interagieren Hintergrundthreads dann mit dem Benutzer?If only one thread can modify the UIUI, how do background threads interact with the user? Ein Hintergrundthread lassen die UIUI Thread zum Ausführen eines Vorgangs in dessen Auftrag aufzubauen.A background thread can ask the UIUI thread to perform an operation on its behalf. Dies geschieht durch Registrieren einer Arbeitsaufgabe mit der Dispatcher von der UIUI Thread.It does this by registering a work item with the Dispatcher of the UIUI thread. Die Dispatcher Klasse bietet zwei Methoden zum Registrieren von Arbeitsaufgaben: Invoke und BeginInvoke.The Dispatcher class provides two methods for registering work items: Invoke and BeginInvoke. Beide Methoden planen einen Delegaten für die Ausführung ein.Both methods schedule a delegate for execution. Invoke ist ein synchroner Aufruf – d. h. die Rückgabe erst die UIUI Threads tatsächlich beendet hat, den Delegaten ausführt.Invoke is a synchronous call – that is, it doesn’t return until the UIUI thread actually finishes executing the delegate. BeginInvoke ist asynchron und gibt sofort zurück.BeginInvoke is asynchronous and returns immediately.

Die Dispatcher sind die Elemente in seiner Warteschlange nach Priorität sortiert.The Dispatcher orders the elements in its queue by priority. Es gibt zehn Stufen, die beim Hinzufügen eines Elements angegeben werden können die Dispatcher Warteschlange.There are ten levels that may be specified when adding an element to the Dispatcher queue. Diese Prioritäten werden beibehalten, der DispatcherPriority Enumeration.These priorities are maintained in the DispatcherPriority enumeration. Detaillierte Informationen über DispatcherPriority Ebenen finden Sie in der Windows SDKWindows SDK Dokumentation.Detailed information about DispatcherPriority levels can be found in the Windows SDKWindows SDK documentation.

Threads in Aktion: Die BeispieleThreads in Action: The Samples

Eine Singlethread-Anwendung mit einer Berechnung mit langer LaufzeitA Single-Threaded Application with a Long-Running Calculation

Die meisten Grafische Benutzeroberflächen (GUIs)graphical user interfaces (GUIs) verbringen einen Großteil ihrer Zeit im Leerlauf, beim Warten auf Ereignisse, die als Reaktion auf Benutzerinteraktionen generiert werden.Most Grafische Benutzeroberflächen (GUIs)graphical user interfaces (GUIs) spend a large portion of their time idle while waiting for events that are generated in response to user interactions. Mit sorgfältiger Programmierung dieser Zeit im Leerlauf kann konstruktiv genutzt werden, ohne Auswirkungen auf die Reaktionsfähigkeit der UIUI.With careful programming this idle time can be used constructively, without affecting the responsiveness of the UIUI. Die WPFWPF threading-Modell nicht, dass eine Eingabe, um einen Vorgang im unterbrechen die UIUI Thread.The WPFWPF threading model doesn’t allow input to interrupt an operation happening in the UIUI thread. Dies bedeutet, dass Sie müssen Sie sicherstellen, dass zum Zurückgeben der Dispatcher in regelmäßigen Abständen den Prozess ausstehende Eingabeereignisse, bevor sie veralten.This means you must be sure to return to the Dispatcher periodically to process pending input events before they get stale.

Betrachten Sie das folgende Beispiel:Consider the following example:

Screenshot mit threading von Primzahlen.

Diese einfache Anwendung zählt ab drei aufwärts und sucht dabei nach Primzahlen.This simple application counts upwards from three, searching for prime numbers. Klickt der Benutzer die starten Schaltfläche, um die Suche beginnt.When the user clicks the Start button, the search begins. Wenn das Programm eine Primzahl findet, wird die Benutzeroberfläche mit dieser Entdeckung aktualisiert.When the program finds a prime, it updates the user interface with its discovery. Der Benutzer kann die Suche zu jedem Zeitpunkt beenden.At any point, the user can stop the search.

Obwohl es sich um eine einfache Anwendung handelt, könnte die Suche nach Primzahlen endlos fortgesetzt werden, was einige Probleme bereitet.Although simple enough, the prime number search could go on forever, which presents some difficulties. Wenn wir die gesamte Suche innerhalb der Click-Ereignishandler der Schaltfläche verarbeitet, würden wir nie geben die UIUI thread die Möglichkeit, andere Ereignisse zu verarbeiten.If we handled the entire search inside of the click event handler of the button, we would never give the UIUI thread a chance to handle other events. Die UIUI nicht zur Reaktion auf Eingabe oder Prozess Nachrichten.The UIUI would be unable to respond to input or process messages. Sie würde nie neu zeichnen und nie auf Mausklicks auf die Schaltflächen reagieren.It would never repaint and never respond to button clicks.

Wir könnten die Suche nach Primzahlen in einem separaten Thread ausführen, aber dann hätten wir Synchronisierungsprobleme.We could conduct the prime number search in a separate thread, but then we would need to deal with synchronization issues. Mit einem Singlethread-Ansatz können wir die Bezeichnung, die die größte gefundene Primzahl auflistet, direkt aktualisieren.With a single-threaded approach, we can directly update the label that lists the largest prime found.

Wenn der Task, der Berechnung in verwaltbare Teile aufgeteilt wird, können wir in regelmäßigen Abständen zurück zu den Dispatcher und Verarbeiten von Ereignissen.If we break up the task of calculation into manageable chunks, we can periodically return to the Dispatcher and process events. Können wir WPFWPF Gelegenheit, zu zeichnen und Eingaben zu verarbeiten.We can give WPFWPF an opportunity to repaint and process input.

Die beste Möglichkeit, Verarbeitungszeit auf Berechnung und Ereignisbehandlung aufzuteilen wird zum Verwalten der Berechnung von der die Dispatcher.The best way to split processing time between calculation and event handling is to manage calculation from the Dispatcher. Mithilfe der BeginInvoke -Methode, wir können primzahlüberprüfungen in identisch, die in die Warteschlange UIUI -Ereignisse stammen.By using the BeginInvoke method, we can schedule prime number checks in the same queue that UIUI events are drawn from. In unserem Beispiel planen wir nur eine einzige Primzahlüberprüfung zu einem Zeitpunkt ein.In our example, we schedule only a single prime number check at a time. Nach Abschluss der Primzahlüberprüfung planen wir sofort die nächste Überprüfung ein.After the prime number check is complete, we schedule the next check immediately. Diese Überprüfung erfolgt erst, wenn ausstehende UIUI behandelt wurden.This check proceeds only after pending UIUI events have been handled.

Screenshot mit der Dispatcher-Warteschlange.

Microsoft WordMicrosoft Word führt die Rechtschreibprüfung mithilfe dieses Mechanismus durch.accomplishes spell checking using this mechanism. Rechtschreibprüfung erfolgt im Hintergrund die Leerlaufzeit an, der über die UIUI Thread.Spell checking is done in the background using the idle time of the UIUI thread. Sehen wir uns den Code an.Let's take a look at the code.

Das folgende Beispiel zeigt die XAML, die die Benutzeroberfläche erstellt.The following example shows the XAML that creates the user interface.

<Window x:Class="SDKSamples.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Prime Numbers" Width="260" Height="75"
    >
  <StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
    <Button Content="Start"  
            Click="StartOrStop"
            Name="startStopButton"
            Margin="5,0,5,0"
            />
    <TextBlock Margin="10,5,0,0">Biggest Prime Found:</TextBlock>
    <TextBlock Name="bigPrime" Margin="4,5,0,0">3</TextBlock>
  </StackPanel>
</Window>

Im folgenden Beispiel wird das CodeBehind gezeigt.The following example shows the code-behind.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Threading;

namespace SDKSamples
{
    public partial class Window1 : Window
    {
        public delegate void NextPrimeDelegate();
        
        //Current number to check 
        private long num = 3;   

        private bool continueCalculating = false;

        public Window1() : base()
        {
            InitializeComponent();
        }

        private void StartOrStop(object sender, EventArgs e)
        {
            if (continueCalculating)
            {
                continueCalculating = false;
                startStopButton.Content = "Resume";
            }
            else
            {
                continueCalculating = true;
                startStopButton.Content = "Stop";
                startStopButton.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new NextPrimeDelegate(CheckNextNumber));
            }
        }

        public void CheckNextNumber()
        {
            // Reset flag.
            NotAPrime = false;

            for (long i = 3; i <= Math.Sqrt(num); i++)
            {
                if (num % i == 0)
                {
                    // Set not a prime flag to true.
                    NotAPrime = true;
                    break;
                }
            }

            // If a prime number.
            if (!NotAPrime)
            {
                bigPrime.Text = num.ToString();
            }

            num += 2;
            if (continueCalculating)
            {
                startStopButton.Dispatcher.BeginInvoke(
                    System.Windows.Threading.DispatcherPriority.SystemIdle, 
                    new NextPrimeDelegate(this.CheckNextNumber));
            }
        }
        
        private bool NotAPrime = false;
    }
}
Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Threading
Imports System.Threading

Namespace SDKSamples
    Partial Public Class MainWindow
        Inherits Window
        Public Delegate Sub NextPrimeDelegate()

        'Current number to check 
        Private num As Long = 3

        Private continueCalculating As Boolean = False

        Public Sub New()
            MyBase.New()
            InitializeComponent()
        End Sub

        Private Sub StartOrStop(ByVal sender As Object, ByVal e As EventArgs)
            If continueCalculating Then
                continueCalculating = False
                startStopButton.Content = "Resume"
            Else
                continueCalculating = True
                startStopButton.Content = "Stop"
                startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New NextPrimeDelegate(AddressOf CheckNextNumber))
            End If
        End Sub

        Public Sub CheckNextNumber()
            ' Reset flag.
            NotAPrime = False

            For i As Long = 3 To Math.Sqrt(num)
                If num Mod i = 0 Then
                    ' Set not a prime flag to true.
                    NotAPrime = True
                    Exit For
                End If
            Next

            ' If a prime number.
            If Not NotAPrime Then
                bigPrime.Text = num.ToString()
            End If

            num += 2
            If continueCalculating Then
                startStopButton.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, New NextPrimeDelegate(AddressOf Me.CheckNextNumber))
            End If
        End Sub

        Private NotAPrime As Boolean = False
    End Class
End Namespace

Das folgende Beispiel zeigt den Ereignishandler für die Button.The following example shows the event handler for the Button.

private void StartOrStop(object sender, EventArgs e)
{
    if (continueCalculating)
    {
        continueCalculating = false;
        startStopButton.Content = "Resume";
    }
    else
    {
        continueCalculating = true;
        startStopButton.Content = "Stop";
        startStopButton.Dispatcher.BeginInvoke(
            DispatcherPriority.Normal,
            new NextPrimeDelegate(CheckNextNumber));
    }
}
Private Sub StartOrStop(ByVal sender As Object, ByVal e As EventArgs)
    If continueCalculating Then
        continueCalculating = False
        startStopButton.Content = "Resume"
    Else
        continueCalculating = True
        startStopButton.Content = "Stop"
        startStopButton.Dispatcher.BeginInvoke(DispatcherPriority.Normal, New NextPrimeDelegate(AddressOf CheckNextNumber))
    End If
End Sub

Neben dem Aktualisieren des Textes auf der Button, dieser Handler ist verantwortlich für die Planung der ersten primzahlüberprüfung durch Hinzufügen eines Delegaten, der Dispatcher Warteschlange.Besides updating the text on the Button, this handler is responsible for scheduling the first prime number check by adding a delegate to the Dispatcher queue. Manchmal, nachdem dieser Ereignishandler seine Arbeit abgeschlossen hat die Dispatcher dieses Delegaten für die Ausführung auswählen.Sometime after this event handler has completed its work, the Dispatcher will select this delegate for execution.

Wie bereits erwähnt BeginInvoke ist die Dispatcher Member verwendet, um einen Delegaten für die Ausführung planen.As we mentioned earlier, BeginInvoke is the Dispatcher member used to schedule a delegate for execution. In diesem Fall wählen wir die SystemIdle Priorität.In this case, we choose the SystemIdle priority. Die Dispatcher führt diesen Delegaten nur, wenn keine wichtigen Ereignisse zu verarbeiten.The Dispatcher will execute this delegate only when there are no important events to process. UIUI-Reaktionsfähigkeit ist wichtiger als die Zahlenüberprüfung.responsiveness is more important than number checking. Wir übergeben auch einen neuen Delegaten, der die Zahlenüberprüfungsroutine darstellt.We also pass a new delegate representing the number-checking routine.

public void CheckNextNumber()
{
    // Reset flag.
    NotAPrime = false;

    for (long i = 3; i <= Math.Sqrt(num); i++)
    {
        if (num % i == 0)
        {
            // Set not a prime flag to true.
            NotAPrime = true;
            break;
        }
    }

    // If a prime number.
    if (!NotAPrime)
    {
        bigPrime.Text = num.ToString();
    }

    num += 2;
    if (continueCalculating)
    {
        startStopButton.Dispatcher.BeginInvoke(
            System.Windows.Threading.DispatcherPriority.SystemIdle, 
            new NextPrimeDelegate(this.CheckNextNumber));
    }
}

private bool NotAPrime = false;
Public Sub CheckNextNumber()
    ' Reset flag.
    NotAPrime = False

    For i As Long = 3 To Math.Sqrt(num)
        If num Mod i = 0 Then
            ' Set not a prime flag to true.
            NotAPrime = True
            Exit For
        End If
    Next

    ' If a prime number.
    If Not NotAPrime Then
        bigPrime.Text = num.ToString()
    End If

    num += 2
    If continueCalculating Then
        startStopButton.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.SystemIdle, New NextPrimeDelegate(AddressOf Me.CheckNextNumber))
    End If
End Sub

Private NotAPrime As Boolean = False

Diese Methode überprüft, ob die nächste ungerade Zahl eine Primzahl ist.This method checks if the next odd number is prime. Wenn sie eine Primzahl ist, aktualisiert die Methode direkt die bigPrime TextBlock entsprechend.If it is prime, the method directly updates the bigPrimeTextBlock to reflect its discovery. Dies ist möglich, da die Berechnung im selben Thread ausgeführt wird, der für die Erstellung der Komponente verwendet wurde.We can do this because the calculation is occurring in the same thread that was used to create the component. Hatten wir uns entschieden, einen separaten Thread für die Berechnung verwendet, müssten wir einen komplizierteren Synchronisierungsmechanismus verwenden, und führen Sie das Update in der UIUI Thread.Had we chosen to use a separate thread for the calculation, we would have to use a more complicated synchronization mechanism and execute the update in the UIUI thread. Wir werden diese Situation im Folgenden zeigen.We’ll demonstrate this situation next.

Den vollständigen Quellcode für dieses Beispiel, finden Sie unter den Single-Threaded Application with Long-Running Calculation SampleFor the complete source code for this sample, see the Single-Threaded Application with Long-Running Calculation Sample

Behandeln eines blockierenden Vorgangs mit einem HintergrundthreadHandling a Blocking Operation with a Background Thread

Die Behandlung von blockierenden Vorgängen in einer grafischen Anwendung kann schwierig sein.Handling blocking operations in a graphical application can be difficult. Wir wollen keine blockierenden Methoden von Ereignishandlern aufrufen, da die Anwendung sonst scheinbar einfriert.We don’t want to call blocking methods from event handlers because the application will appear to freeze up. Wir können einen separaten Thread verwenden, um diese Vorgänge zu behandeln, aber wenn wir fertig sind, haben wir zum Synchronisieren mit der UIUI thread auf, da wir nicht direkt ändern können die GUIGUI vom Arbeitsthread aus.We can use a separate thread to handle these operations, but when we’re done, we have to synchronize with the UIUI thread because we can’t directly modify the GUIGUI from our worker thread. Wir können Invoke oder BeginInvoke zum Einfügen von Delegaten in der Dispatcher von der UIUI Thread.We can use Invoke or BeginInvoke to insert delegates into the Dispatcher of the UIUI thread. Schließlich werden diese Delegaten ausgeführt werden, mit der Berechtigung zum Ändern der UIUI Elemente.Eventually, these delegates will be executed with permission to modify UIUI elements.

In diesem Beispiel simulieren wir einen Remoteprozeduraufruf, der eine Wettervorhersage abruft.In this example, we mimic a remote procedure call that retrieves a weather forecast. Wir verwenden einen separaten Arbeitsthread aus, um die Ausführung dieses Aufrufs und planen wir eine Updatemethode in der Dispatcher von der UIUI Threads ein, wenn wir fertig sind.We use a separate worker thread to execute this call, and we schedule an update method in the Dispatcher of the UIUI thread when we’re finished.

Screenshot mit dem Wetter-UI.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;

namespace SDKSamples
{
    public partial class Window1 : Window
    {
        // Delegates to be used in placking jobs onto the Dispatcher.
        private delegate void NoArgDelegate();
        private delegate void OneArgDelegate(String arg);

        // Storyboards for the animations.
        private Storyboard showClockFaceStoryboard;
        private Storyboard hideClockFaceStoryboard;
        private Storyboard showWeatherImageStoryboard;
        private Storyboard hideWeatherImageStoryboard;

        public Window1(): base()
        {
            InitializeComponent();
        }  

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Load the storyboard resources.
            showClockFaceStoryboard = 
                (Storyboard)this.Resources["ShowClockFaceStoryboard"];
            hideClockFaceStoryboard = 
                (Storyboard)this.Resources["HideClockFaceStoryboard"];
            showWeatherImageStoryboard = 
                (Storyboard)this.Resources["ShowWeatherImageStoryboard"];
            hideWeatherImageStoryboard = 
                (Storyboard)this.Resources["HideWeatherImageStoryboard"];   
        }

        private void ForecastButtonHandler(object sender, RoutedEventArgs e)
        {
            // Change the status image and start the rotation animation.
            fetchButton.IsEnabled = false;
            fetchButton.Content = "Contacting Server";
            weatherText.Text = "";
            hideWeatherImageStoryboard.Begin(this);
            
            // Start fetching the weather forecast asynchronously.
            NoArgDelegate fetcher = new NoArgDelegate(
                this.FetchWeatherFromServer);

            fetcher.BeginInvoke(null, null);
        }

        private void FetchWeatherFromServer()
        {
            // Simulate the delay from network access.
            Thread.Sleep(4000);              
            
            // Tried and true method for weather forecasting - random numbers.
            Random rand = new Random();
            String weather;

            if (rand.Next(2) == 0)
            {
                weather = "rainy";
            }
            else
            {
                weather = "sunny";
            }

            // Schedule the update function in the UI thread.
            tomorrowsWeather.Dispatcher.BeginInvoke(
                System.Windows.Threading.DispatcherPriority.Normal,
                new OneArgDelegate(UpdateUserInterface), 
                weather);
        }

        private void UpdateUserInterface(String weather)
        {    
            //Set the weather image
            if (weather == "sunny")
            {       
                weatherIndicatorImage.Source = (ImageSource)this.Resources[
                    "SunnyImageSource"];
            }
            else if (weather == "rainy")
            {
                weatherIndicatorImage.Source = (ImageSource)this.Resources[
                    "RainingImageSource"];
            }

            //Stop clock animation
            showClockFaceStoryboard.Stop(this);
            hideClockFaceStoryboard.Begin(this);

            //Update UI text
            fetchButton.IsEnabled = true;
            fetchButton.Content = "Fetch Forecast";
            weatherText.Text = weather;     
        }

        private void HideClockFaceStoryboard_Completed(object sender,
            EventArgs args)
        {         
            showWeatherImageStoryboard.Begin(this);
        }
        
        private void HideWeatherImageStoryboard_Completed(object sender,
            EventArgs args)
        {           
            showClockFaceStoryboard.Begin(this, true);
        }        
    }
}

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.Windows.Media.Animation
Imports System.Windows.Media.Imaging
Imports System.Windows.Shapes
Imports System.Windows.Threading
Imports System.Threading

Namespace SDKSamples
    Partial Public Class Window1
        Inherits Window
        ' Delegates to be used in placking jobs onto the Dispatcher.
        Private Delegate Sub NoArgDelegate()
        Private Delegate Sub OneArgDelegate(ByVal arg As String)

        ' Storyboards for the animations.
        Private showClockFaceStoryboard As Storyboard
        Private hideClockFaceStoryboard As Storyboard
        Private showWeatherImageStoryboard As Storyboard
        Private hideWeatherImageStoryboard As Storyboard

        Public Sub New()
            MyBase.New()
            InitializeComponent()
        End Sub

        Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Load the storyboard resources.
            showClockFaceStoryboard = CType(Me.Resources("ShowClockFaceStoryboard"), Storyboard)
            hideClockFaceStoryboard = CType(Me.Resources("HideClockFaceStoryboard"), Storyboard)
            showWeatherImageStoryboard = CType(Me.Resources("ShowWeatherImageStoryboard"), Storyboard)
            hideWeatherImageStoryboard = CType(Me.Resources("HideWeatherImageStoryboard"), Storyboard)
        End Sub

        Private Sub ForecastButtonHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Change the status image and start the rotation animation.
            fetchButton.IsEnabled = False
            fetchButton.Content = "Contacting Server"
            weatherText.Text = ""
            hideWeatherImageStoryboard.Begin(Me)

            ' Start fetching the weather forecast asynchronously.
            Dim fetcher As New NoArgDelegate(AddressOf Me.FetchWeatherFromServer)

            fetcher.BeginInvoke(Nothing, Nothing)
        End Sub

        Private Sub FetchWeatherFromServer()
            ' Simulate the delay from network access.
            Thread.Sleep(4000)

            ' Tried and true method for weather forecasting - random numbers.
            Dim rand As New Random()
            Dim weather As String

            If rand.Next(2) = 0 Then
                weather = "rainy"
            Else
                weather = "sunny"
            End If

            ' Schedule the update function in the UI thread.
            tomorrowsWeather.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, New OneArgDelegate(AddressOf UpdateUserInterface), weather)
        End Sub

        Private Sub UpdateUserInterface(ByVal weather As String)
            'Set the weather image
            If weather = "sunny" Then
                weatherIndicatorImage.Source = CType(Me.Resources("SunnyImageSource"), ImageSource)
            ElseIf weather = "rainy" Then
                weatherIndicatorImage.Source = CType(Me.Resources("RainingImageSource"), ImageSource)
            End If

            'Stop clock animation
            showClockFaceStoryboard.Stop(Me)
            hideClockFaceStoryboard.Begin(Me)

            'Update UI text
            fetchButton.IsEnabled = True
            fetchButton.Content = "Fetch Forecast"
            weatherText.Text = weather
        End Sub

        Private Sub HideClockFaceStoryboard_Completed(ByVal sender As Object, ByVal args As EventArgs)
            showWeatherImageStoryboard.Begin(Me)
        End Sub

        Private Sub HideWeatherImageStoryboard_Completed(ByVal sender As Object, ByVal args As EventArgs)
            showClockFaceStoryboard.Begin(Me, True)
        End Sub
    End Class
End Namespace

Im Folgenden sind einige der Details aufgeführt, die beachtet werden sollten.The following are some of the details to be noted.

  • Erstellen des SchaltflächenhandlersCreating the Button Handler

    private void ForecastButtonHandler(object sender, RoutedEventArgs e)
    {
        // Change the status image and start the rotation animation.
        fetchButton.IsEnabled = false;
        fetchButton.Content = "Contacting Server";
        weatherText.Text = "";
        hideWeatherImageStoryboard.Begin(this);
        
        // Start fetching the weather forecast asynchronously.
        NoArgDelegate fetcher = new NoArgDelegate(
            this.FetchWeatherFromServer);
    
        fetcher.BeginInvoke(null, null);
    }
    
    Private Sub ForecastButtonHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Change the status image and start the rotation animation.
        fetchButton.IsEnabled = False
        fetchButton.Content = "Contacting Server"
        weatherText.Text = ""
        hideWeatherImageStoryboard.Begin(Me)
    
        ' Start fetching the weather forecast asynchronously.
        Dim fetcher As New NoArgDelegate(AddressOf Me.FetchWeatherFromServer)
    
        fetcher.BeginInvoke(Nothing, Nothing)
    End Sub
    

Wenn auf die Schaltfläche geklickt wird, wird die Uhr-Zeichnung angezeigt, und wir beginnen mit der Animation.When the button is clicked, we display the clock drawing and start animating it. Die Schaltfläche wird deaktiviert.We disable the button. Rufen wir die FetchWeatherFromServer -Methode in einen neuen Thread und anschließend zurück, sodass die Dispatcher zum Verarbeiten von Ereignissen während wir auf die Wettervorhersage warten.We invoke the FetchWeatherFromServer method in a new thread, and then we return, allowing the Dispatcher to process events while we wait to collect the weather forecast.

  • Abrufen der WettervorhersageFetching the Weather

    private void FetchWeatherFromServer()
    {
        // Simulate the delay from network access.
        Thread.Sleep(4000);              
        
        // Tried and true method for weather forecasting - random numbers.
        Random rand = new Random();
        String weather;
    
        if (rand.Next(2) == 0)
        {
            weather = "rainy";
        }
        else
        {
            weather = "sunny";
        }
    
        // Schedule the update function in the UI thread.
        tomorrowsWeather.Dispatcher.BeginInvoke(
            System.Windows.Threading.DispatcherPriority.Normal,
            new OneArgDelegate(UpdateUserInterface), 
            weather);
    }
    
    Private Sub FetchWeatherFromServer()
        ' Simulate the delay from network access.
        Thread.Sleep(4000)
    
        ' Tried and true method for weather forecasting - random numbers.
        Dim rand As New Random()
        Dim weather As String
    
        If rand.Next(2) = 0 Then
            weather = "rainy"
        Else
            weather = "sunny"
        End If
    
        ' Schedule the update function in the UI thread.
        tomorrowsWeather.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, New OneArgDelegate(AddressOf UpdateUserInterface), weather)
    End Sub
    

Aus Gründen der Einfachheit verwenden wir in diesem Beispiel keinen Netzwerk-Code.To keep things simple, we don’t actually have any networking code in this example. Stattdessen simulieren wir die Verzögerung des Netzwerkzugriffs, indem wir den neuen Thread für vier Sekunden in den Ruhezustand versetzen.Instead, we simulate the delay of network access by putting our new thread to sleep for four seconds. In dieser Zeit wird die ursprüngliche UIUI Thread noch ausgeführt wird und auf Ereignisse reagieren.In this time, the original UIUI thread is still running and responding to events. Um dies zu zeigen, führen wir eine Animation weiterhin aus, und die Schaltflächen „Minimieren“ und „Maximieren“ funktionieren ebenfalls weiterhin.To show this, we’ve left an animation running, and the minimize and maximize buttons also continue to work.

Wenn die Verzögerung beendet, und wir die Wettervorhersage zufällig ausgewählt haben, ist es Zeit, zurückzumelden der UIUI Thread.When the delay is finished, and we’ve randomly selected our weather forecast, it’s time to report back to the UIUI thread. Wir erreichen dies durch Planen eines Aufrufs von UpdateUserInterface in die UIUI thread mithilfe des betreffenden Threads Dispatcher.We do this by scheduling a call to UpdateUserInterface in the UIUI thread using that thread’s Dispatcher. Wir übergeben eine Zeichenfolge, die das Wetter beschreibt, an diesen eingeplanten Methodenaufruf.We pass a string describing the weather to this scheduled method call.

  • Aktualisieren der UIUIUpdating the UIUI

    private void UpdateUserInterface(String weather)
    {    
        //Set the weather image
        if (weather == "sunny")
        {       
            weatherIndicatorImage.Source = (ImageSource)this.Resources[
                "SunnyImageSource"];
        }
        else if (weather == "rainy")
        {
            weatherIndicatorImage.Source = (ImageSource)this.Resources[
                "RainingImageSource"];
        }
    
        //Stop clock animation
        showClockFaceStoryboard.Stop(this);
        hideClockFaceStoryboard.Begin(this);
    
        //Update UI text
        fetchButton.IsEnabled = true;
        fetchButton.Content = "Fetch Forecast";
        weatherText.Text = weather;     
    }
    
    Private Sub UpdateUserInterface(ByVal weather As String)
        'Set the weather image
        If weather = "sunny" Then
            weatherIndicatorImage.Source = CType(Me.Resources("SunnyImageSource"), ImageSource)
        ElseIf weather = "rainy" Then
            weatherIndicatorImage.Source = CType(Me.Resources("RainingImageSource"), ImageSource)
        End If
    
        'Stop clock animation
        showClockFaceStoryboard.Stop(Me)
        hideClockFaceStoryboard.Begin(Me)
    
        'Update UI text
        fetchButton.IsEnabled = True
        fetchButton.Content = "Fetch Forecast"
        weatherText.Text = weather
    End Sub
    

Wenn die Dispatcher in die UIUI Thread Zeit hat, führt er den eingeplanten Aufruf von UpdateUserInterface.When the Dispatcher in the UIUI thread has time, it executes the scheduled call to UpdateUserInterface. Diese Methode hält die Uhr-Animation an und wählt ein Bild aus, um das Wetter zu beschreiben.This method stops the clock animation and chooses an image to describe the weather. Sie zeigt dieses Bild an und stellt die Schaltfläche „fetch forecast“ (Wettervorhersage abrufen) wieder her.It displays this image and restores the "fetch forecast" button.

Mehrere Fenster, mehrere ThreadsMultiple Windows, Multiple Threads

Einige WPFWPF Anwendungen erfordern mehrere Fenster von der obersten Ebene.Some WPFWPF applications require multiple top-level windows. Es ist durchaus akzeptabel, dass ein Thread /Dispatcher Tastenkombination, um mehrere Fenster verwaltet, aber manchmal mehrere Threads besser funktionieren.It is perfectly acceptable for one Thread/Dispatcher combination to manage multiple windows, but sometimes several threads do a better job. Dies trifft besonders zu, wenn die Möglichkeit besteht, dass eines der Fenster den Thread für sich beansprucht.This is especially true if there is any chance that one of the windows will monopolize the thread.

WindowsWindows Explorer funktioniert auf diese Weise.Explorer works in this fashion. Jedes neue Explorer-Fenster gehört zum ursprünglichen Prozess, wird jedoch unter der Kontrolle eines unabhängigen Threads erstellt.Each new Explorer window belongs to the original process, but it is created under the control of an independent thread.

Mithilfe einer WPFWPF Frame -Steuerelement, können wir Webseiten anzeigen.By using a WPFWPFFrame control, we can display Web pages. Wir können problemlos erstellen eine einfache Internet ExplorerInternet Explorer ersetzen.We can easily create a simple Internet ExplorerInternet Explorer substitute. Wir beginnen mit einer wichtigen Funktion, und zwar der Möglichkeit, ein neues Explorerfenster zu öffnen.We start with an important feature: the ability to open a new explorer window. Wenn der Benutzer auf die Schaltfläche „Neues Fenster“ klickt, starten wir eine Kopie des Fensters in einem separaten Thread.When the user clicks the "new window" button, we launch a copy of our window in a separate thread. Auf diese Weise sperren lange andauernde oder blockierende Vorgänge in einem der Fenster nicht alle anderen Fenster.This way, long-running or blocking operations in one of the windows won’t lock all the other windows.

In Wirklichkeit verfügt das Webbrowser-Modell über ein eigenes kompliziertes Threadingmodell.In reality, the Web browser model has its own complicated threading model. Wir haben uns dafür entschieden, da die meisten Leser damit vertraut sein sollten.We’ve chosen it because it should be familiar to most readers.

Im folgenden Beispiel wird der Code angezeigt.The following example shows the code.

<Window x:Class="SDKSamples.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MultiBrowse"
    Height="600" 
    Width="800"
    Loaded="OnLoaded"
    >
  <StackPanel Name="Stack" Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
      <Button Content="New Window"
              Click="NewWindowHandler" />
      <TextBox Name="newLocation"
               Width="500" />
      <Button Content="GO!"
              Click="Browse" />
    </StackPanel>

    <Frame Name="placeHolder"
            Width="800"
            Height="550"></Frame>
  </StackPanel>
</Window>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Threading;
using System.Threading;


namespace SDKSamples
{
    public partial class Window1 : Window
    {

        public Window1() : base()
        {
            InitializeComponent();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
           placeHolder.Source = new Uri("http://www.msn.com");
        }

        private void Browse(object sender, RoutedEventArgs e)
        {
            placeHolder.Source = new Uri(newLocation.Text);
        }

        private void NewWindowHandler(object sender, RoutedEventArgs e)
        {       
            Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
            newWindowThread.SetApartmentState(ApartmentState.STA);
            newWindowThread.IsBackground = true;
            newWindowThread.Start();
        }

        private void ThreadStartingPoint()
        {
            Window1 tempWindow = new Window1();
            tempWindow.Show();       
            System.Windows.Threading.Dispatcher.Run();
        }
    }
}

Imports System
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Data
Imports System.Windows.Threading
Imports System.Threading


Namespace SDKSamples
    Partial Public Class Window1
        Inherits Window

        Public Sub New()
            MyBase.New()
            InitializeComponent()
        End Sub

        Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
           placeHolder.Source = New Uri("http://www.msn.com")
        End Sub

        Private Sub Browse(ByVal sender As Object, ByVal e As RoutedEventArgs)
            placeHolder.Source = New Uri(newLocation.Text)
        End Sub

        Private Sub NewWindowHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim newWindowThread As New Thread(New ThreadStart(AddressOf ThreadStartingPoint))
            newWindowThread.SetApartmentState(ApartmentState.STA)
            newWindowThread.IsBackground = True
            newWindowThread.Start()
        End Sub

        Private Sub ThreadStartingPoint()
            Dim tempWindow As New Window1()
            tempWindow.Show()
            System.Windows.Threading.Dispatcher.Run()
        End Sub
    End Class
End Namespace

Die folgenden Threadingsegmente dieses Codes sind für uns in diesem Kontext am interessantesten:The following threading segments of this code are the most interesting to us in this context:

private void NewWindowHandler(object sender, RoutedEventArgs e)
{       
    Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
    newWindowThread.SetApartmentState(ApartmentState.STA);
    newWindowThread.IsBackground = true;
    newWindowThread.Start();
}
Private Sub NewWindowHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim newWindowThread As New Thread(New ThreadStart(AddressOf ThreadStartingPoint))
    newWindowThread.SetApartmentState(ApartmentState.STA)
    newWindowThread.IsBackground = True
    newWindowThread.Start()
End Sub

Diese Methode wird aufgerufen, wenn die Schaltfläche „Neues Fenster“ angeklickt wird.This method is called when the "new window" button is clicked. Sie erstellt einen neuen Thread und startet diesen asynchron.It creates a new thread and starts it asynchronously.

private void ThreadStartingPoint()
{
    Window1 tempWindow = new Window1();
    tempWindow.Show();       
    System.Windows.Threading.Dispatcher.Run();
}
Private Sub ThreadStartingPoint()
    Dim tempWindow As New Window1()
    tempWindow.Show()
    System.Windows.Threading.Dispatcher.Run()
End Sub

Diese Methode ist der Ausgangspunkt für den neuen Thread.This method is the starting point for the new thread. Wir erstellen ein neues Fenster unter der Kontrolle dieses Threads.We create a new window under the control of this thread. WPFWPF erstellt automatisch eine neue Dispatcher um den neuen Thread zu verwalten.automatically creates a new Dispatcher to manage the new thread. Alles, was wir tun, um das Fenster funktionsfähig ist, starten Sie den Dispatcher.All we have to do to make the window functional is to start the Dispatcher.

Technische Details und StolpersteineTechnical Details and Stumbling Points

Schreiben von Komponenten mithilfe von ThreadingWriting Components Using Threading

Microsoft .NET Framework Developer's Guide Beschreibt ein Muster wie eine Komponente asynchrones Verhalten für die Clients verfügbar machen kann (siehe Übersicht über ereignisbasierte asynchrone Muster).The Microsoft .NET Framework Developer's Guide describes a pattern for how a component can expose asynchronous behavior to its clients (see Event-based Asynchronous Pattern Overview). Nehmen wir beispielsweise an, wir wollten zum Packen der FetchWeatherFromServer Methode in eine wiederverwendbare, nichtgrafische Komponente.For instance, suppose we wanted to package the FetchWeatherFromServer method into a reusable, nongraphical component. Nach dem standardmäßigen Microsoft .NET Framework-Muster würde dies etwa wie folgt aussehen.Following the standard Microsoft .NET Framework pattern, this would look something like the following.

public class WeatherComponent : Component
{
    //gets weather: Synchronous 
    public string GetWeather()
    {
        string weather = "";

        //predict the weather

        return weather;
    }

    //get weather: Asynchronous 
    public void GetWeatherAsync()
    {
        //get the weather
    }

    public event GetWeatherCompletedEventHandler GetWeatherCompleted;
}

public class GetWeatherCompletedEventArgs : AsyncCompletedEventArgs
{
    public GetWeatherCompletedEventArgs(Exception error, bool canceled,
        object userState, string weather)
        :
        base(error, canceled, userState)
    {
        _weather = weather;
    }

    public string Weather
    {
        get { return _weather; }
    }
    private string _weather;
}

public delegate void GetWeatherCompletedEventHandler(object sender,
    GetWeatherCompletedEventArgs e);
Public Class WeatherComponent
    Inherits Component
    'gets weather: Synchronous 
    Public Function GetWeather() As String
        Dim weather As String = ""

        'predict the weather

        Return weather
    End Function

    'get weather: Asynchronous 
    Public Sub GetWeatherAsync()
        'get the weather
    End Sub

    Public Event GetWeatherCompleted As GetWeatherCompletedEventHandler
End Class

Public Class GetWeatherCompletedEventArgs
    Inherits AsyncCompletedEventArgs
    Public Sub New(ByVal [error] As Exception, ByVal canceled As Boolean, ByVal userState As Object, ByVal weather As String)
        MyBase.New([error], canceled, userState)
        _weather = weather
    End Sub

    Public ReadOnly Property Weather() As String
        Get
            Return _weather
        End Get
    End Property
    Private _weather As String
End Class

Public Delegate Sub GetWeatherCompletedEventHandler(ByVal sender As Object, ByVal e As GetWeatherCompletedEventArgs)

GetWeatherAsync würde eine der zuvor beschriebenen Techniken wie z.B. das Erstellen eines Hintergrundthreads verwenden, um die Arbeit asynchron auszuführen und den aufrufenden Thread nicht zu blockieren.GetWeatherAsync would use one of the techniques described earlier, such as creating a background thread, to do the work asynchronously, not blocking the calling thread.

Einer der wichtigsten Teile dieses Musters ist der Aufruf der MethodName Completed Methode auf dem gleichen Thread, der Namen der MethodName Async Methode damit beginnt, mit.One of the most important parts of this pattern is calling the MethodNameCompleted method on the same thread that called the MethodNameAsync method to begin with. Könnte dazu verwenden Sie WPFWPF relativ einfach erreichen, indem Sie speichern CurrentDispatcher– klicken Sie dann die nichtgrafische Komponente kann nur verwendet werden, aber WPFWPF Anwendungen nicht in Windows FormsWindows Forms oder ASP.NETASP.NET Programme.You could do this using WPFWPF fairly easily, by storing CurrentDispatcher—but then the nongraphical component could only be used in WPFWPF applications, not in Windows FormsWindows Forms or ASP.NETASP.NET programs.

Der DispatcherSynchronizationContext -Klasse erfüllt diese Anforderung — betrachten Sie sie als eine vereinfachte Version der Dispatcher dieses Produkt funktioniert mit anderen UIUI auch Frameworks.The DispatcherSynchronizationContext class addresses this need—think of it as a simplified version of Dispatcher that works with other UIUI frameworks as well.

public class WeatherComponent2 : Component
{
    public string GetWeather()
    {
        return fetchWeatherFromServer();
    }

    private DispatcherSynchronizationContext requestingContext = null;

    public void GetWeatherAsync()
    {
        if (requestingContext != null)
            throw new InvalidOperationException("This component can only handle 1 async request at a time");

        requestingContext = (DispatcherSynchronizationContext)DispatcherSynchronizationContext.Current;

        NoArgDelegate fetcher = new NoArgDelegate(this.fetchWeatherFromServer);

        // Launch thread
        fetcher.BeginInvoke(null, null);
    }

    private void RaiseEvent(GetWeatherCompletedEventArgs e)
    {
        if (GetWeatherCompleted != null)
            GetWeatherCompleted(this, e);
    }

    private string fetchWeatherFromServer()
    {
        // do stuff
        string weather = "";

        GetWeatherCompletedEventArgs e =
            new GetWeatherCompletedEventArgs(null, false, null, weather);

        SendOrPostCallback callback = new SendOrPostCallback(DoEvent);
        requestingContext.Post(callback, e);
        requestingContext = null;

        return e.Weather;
    }

    private void DoEvent(object e)
    {
        //do stuff
    }

    public event GetWeatherCompletedEventHandler GetWeatherCompleted;
    public delegate string NoArgDelegate();
}
Public Class WeatherComponent2
    Inherits Component
    Public Function GetWeather() As String
        Return fetchWeatherFromServer()
    End Function

    Private requestingContext As DispatcherSynchronizationContext = Nothing

    Public Sub GetWeatherAsync()
        If requestingContext IsNot Nothing Then
            Throw New InvalidOperationException("This component can only handle 1 async request at a time")
        End If

        requestingContext = CType(DispatcherSynchronizationContext.Current, DispatcherSynchronizationContext)

        Dim fetcher As New NoArgDelegate(AddressOf Me.fetchWeatherFromServer)

        ' Launch thread
        fetcher.BeginInvoke(Nothing, Nothing)
    End Sub

    Private Sub [RaiseEvent](ByVal e As GetWeatherCompletedEventArgs)
        RaiseEvent GetWeatherCompleted(Me, e)
    End Sub

    Private Function fetchWeatherFromServer() As String
        ' do stuff
        Dim weather As String = ""

        Dim e As New GetWeatherCompletedEventArgs(Nothing, False, Nothing, weather)

        Dim callback As New SendOrPostCallback(AddressOf DoEvent)
        requestingContext.Post(callback, e)
        requestingContext = Nothing

        Return e.Weather
    End Function

    Private Sub DoEvent(ByVal e As Object)
        'do stuff
    End Sub

    Public Event GetWeatherCompleted As GetWeatherCompletedEventHandler
    Public Delegate Function NoArgDelegate() As String
End Class

Geschachtelte VerteilungNested Pumping

Manchmal ist es nicht möglich, völlig gesperrt ist, bis die UIUI Thread.Sometimes it is not feasible to completely lock up the UIUI thread. Betrachten wir die Show Methode der MessageBox Klasse.Let’s consider the Show method of the MessageBox class. Show Gibt nicht zurück, bis der Benutzer die Schaltfläche "OK" klickt.Show doesn’t return until the user clicks the OK button. Es wird jedoch ein Fenster erstellt, das über eine Meldungsschleife verfügen muss, um interaktiv zu sein.It does, however, create a window that must have a message loop in order to be interactive. Während wir warten, bis der Benutzer auf „OK“ klickt, reagiert das ursprüngliche Anwendungsfenster nicht auf Benutzereingaben.While we are waiting for the user to click OK, the original application window does not respond to user input. Es verarbeitet jedoch weiterhin Paint-Meldungen.It does, however, continue to process paint messages. Das ursprüngliche Fenster zeichnet sich selbst neu, wenn es verdeckt war und dann wieder angezeigt wird.The original window redraws itself when covered and revealed.

Screenshot, der eine MessageBox mit Schaltfläche "OK" anzeigt

Ein Thread muss für das Meldungsfenster zuständig sein.Some thread must be in charge of the message box window. WPFWPF könnte einen neuen Thread nur für das Meldungsfenster erstellen, aber dieser Thread wäre nicht in der Lage, die deaktivierten Elemente im ursprünglichen Fenster zu zeichnen (beachten Sie den bereits erwähnten gegenseitigen Ausschluss).could create a new thread just for the message box window, but this thread would be unable to paint the disabled elements in the original window (remember the earlier discussion of mutual exclusion). Stattdessen WPFWPF verwendet ein geschachteltes meldungsverarbeitungssystem.Instead, WPFWPF uses a nested message processing system. Die Dispatcher Klasse enthält eine spezielle Methode namens PushFrame, dem aktuellen Ausführungspunkt in einer Anwendung speichert eine neue Meldungsschleife beginnt.The Dispatcher class includes a special method called PushFrame, which stores an application’s current execution point then begins a new message loop. Nach Abschluss die geschachtelten Meldungsschleife Ausführung fortgesetzt wird, nach dem ursprünglichen PushFrame aufrufen.When the nested message loop finishes, execution resumes after the original PushFrame call.

In diesem Fall PushFrame behält den Programmkontext beim Aufruf MessageBox.Show, und beginnt eine neue Meldungsschleife, um das Hintergrundfenster und Eingaben für das Meldungsfenster zu behandeln.In this case, PushFrame maintains the program context at the call to MessageBox.Show, and it starts a new message loop to repaint the background window and handle input to the message box window. Wenn der Benutzer auf OK klickt und das Popupfenster löscht, die geschachtelte Schleife beendet und die Steuerung wird nach dem Aufruf von Show.When the user clicks OK and clears the pop-up window, the nested loop exits and control resumes after the call to Show.

Veraltete RoutingereignisseStale Routed Events

Das Routingereignissystem in WPFWPF benachrichtigt ganze Strukturen, wenn Ereignisse ausgelöst werden.The routed event system in WPFWPF notifies entire trees when events are raised.

<Canvas MouseLeftButtonDown="handler1" 
        Width="100"
        Height="100"
        >
  <Ellipse Width="50"
           Height="50"
           Fill="Blue" 
           Canvas.Left="30"
           Canvas.Top="50" 
           MouseLeftButtonDown="handler2"
           />
</Canvas>

Wenn die linke Maustaste, über die Ellipse gedrückt wird, handler2 ausgeführt wird.When the left mouse button is pressed over the ellipse, handler2 is executed. Nach dem handler2 beendet ist ist, das Ereignis wird an übergeben, die Canvas -Objekt, das verwendet handler1 , sie zu verarbeiten.After handler2 finishes, the event is passed along to the Canvas object, which uses handler1 to process it. Dies geschieht nur, wenn handler2 ist nicht explizit Mark stattdessen das Ereignis als behandelt.This happens only if handler2 does not explicitly mark the event object as handled.

Es ist möglich, handler2 wird sehr viel Zeit, die dieses Ereignis zu verarbeiten.It’s possible that handler2 will take a great deal of time processing this event. handler2 Verwenden Sie möglicherweise PushFrame um eine geschachtelte Meldungsschleife zu beginnen, die keinen Stunden zurückgibt.handler2 might use PushFrame to begin a nested message loop that doesn’t return for hours. Wenn handler2 nicht kennzeichnet das Ereignis als behandelt, wenn diese Meldungsschleife ist abgeschlossen, wird das Ereignis der Struktur nach oben übergeben, obwohl es sehr alt ist.If handler2 does not mark the event as handled when this message loop is complete, the event is passed up the tree even though it is very old.

Wiedereintritt und SperrungReentrancy and Locking

Der Mechanismus zum Sperren von der Common Language Runtime (CLR)common language runtime (CLR) verhält Sie sich nicht genau wie man sich vorstellen könnte; man könnte erwarten, dass einen Thread beim Anfordern einer Sperre den Vorgang vollständig beendet.The locking mechanism of the Common Language Runtime (CLR)common language runtime (CLR) doesn’t behave exactly as one might imagine; one might expect a thread to cease operation completely when requesting a lock. In Wirklichkeit empfängt und verarbeitet der Thread weiterhin Meldungen mit hoher Priorität.In actuality, the thread continues to receive and process high-priority messages. Dadurch können Deadlocks vermieden und Schnittstellen minimal reaktionsfähig gemacht werden, aber dadurch können auch schwer erkennbare Fehler entstehen.This helps prevent deadlocks and make interfaces minimally responsive, but it introduces the possibility for subtle bugs. Der Großteil der Zeit nicht alles wissen, dass zu diesem, aber in seltenen Fällen müssen (normalerweise im Zusammenhang mit Win32Win32 fenstermeldungen oder COM-STA-Komponenten) Entwurfsalternativen infrage.The vast majority of the time you don’t need to know anything about this, but under rare circumstances (usually involving Win32Win32 window messages or COM STA components) this can be worth knowing.

Die meisten Schnittstellen werden nicht mit der im Hinblick auf Threadsicherheit erstellt, da Entwickler bei Ihrer Arbeit, die eine UIUI nie durch mehrere Threads zugegriffen wird.Most interfaces are not built with thread safety in mind because developers work under the assumption that a UIUI is never accessed by more than one thread. In diesem Fall ist der Singlethread Umweltbelastung zu unerwarteten Zeiten ändern kann diese falsch verursacht, die Auswirkungen der DispatcherObject soll gegenseitige Ausschlussmechanismus um zu lösen.In this case, that single thread may make environmental changes at unexpected times, causing those ill effects that the DispatcherObject mutual exclusion mechanism is supposed to solve. Betrachten Sie den folgenden Pseudocode:Consider the following pseudocode:

Diagramm, das threading Eintrittsinvarianz angezeigt wird. Diagram that shows threading reentrancy.

In den meisten Fällen das ist richtig, aber es gibt Situationen, in WPFWPF , in denen solch ein unerwarteter Wiedereintritt kann wirklich zu Problemen führen.Most of the time that’s the right thing, but there are times in WPFWPF where such unexpected reentrancy can really cause problems. In diesem Fall zu bestimmten Zeiten, WPFWPF Aufrufe DisableProcessing, welche Änderungen die sperranweisung für diesen Thread so die WPFWPF Sperre Wiedereintritt anstatt der üblichen CLRCLR Sperre.So, at certain key times, WPFWPF calls DisableProcessing, which changes the lock instruction for that thread to use the WPFWPF reentrancy-free lock, instead of the usual CLRCLR lock.

Warum wurde die CLRCLR Team dieses Verhalten entschieden?So why did the CLRCLR team choose this behavior? Dies hatte mit COM-STA-Objekten und dem Finalizerthread zu tun.It had to do with COM STA objects and the finalization thread. Wenn ein Objekt mit Garbage Collection, ist die Finalize -Methode nicht auf dem dedizierten Finalizerthread ausgeführt wird die UIUI Thread.When an object is garbage collected, its Finalize method is run on the dedicated finalizer thread, not the UIUI thread. Und darin liegt das Problem, da eine COM-STA-Objekt, das erstellt wurde, auf die UIUI Thread kann nur freigegeben werden, auf die UIUI Thread.And therein lies the problem, because a COM STA object that was created on the UIUI thread can only be disposed on the UIUI thread. Die CLRCLR entspricht einem BeginInvoke (in diesem Fall von Win32 SendMessage).The CLRCLR does the equivalent of a BeginInvoke (in this case using Win32’s SendMessage). Wenn Sie allerdings die UIUI Thread ausgelastet ist, der Finalizer-Thread wurde unterbrochen, und das COM-STA-Objekt nicht verworfen werden, die einen schwerwiegenden Speicherverlust erstellt.But if the UIUI thread is busy, the finalizer thread is stalled and the COM STA object can’t be disposed, which creates a serious memory leak. Daher CLRCLR -Team für diesen Aufruf entschieden, damit Sperren wie gewünscht dies der Fall.So the CLRCLR team made the tough call to make locks work the way they do.

Die Aufgabe für WPFWPF besteht darin, unerwarteten Wiedereintritt zu verhindern, ohne Speicherverlust zu, weshalb wir Wiedereintritt nicht überall blockieren.The task for WPFWPF is to avoid unexpected reentrancy without reintroducing the memory leak, which is why we don’t block reentrancy everywhere.

Siehe auchSee also