달력

5

« 2025/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2016. 11. 5. 14:26

WPF Unmanaged API Reference 프로그래밍/WPF2016. 11. 5. 14:26

WPF Unmanaged API Reference



https://msdn.microsoft.com/ko-kr/library/mt162321(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/mt162321(v=vs.110).aspx




'프로그래밍 > WPF' 카테고리의 다른 글

Control Library 2  (0) 2016.11.13
Controls  (0) 2016.11.05
Threading Model  (0) 2016.11.05
Performance  (0) 2016.11.05
Migration and Interoperability  (0) 2016.11.05
:
Posted by 지훈2
2016. 11. 5. 14:25

Threading Model 프로그래밍/WPF2016. 11. 5. 14:25

Threading Model


https://msdn.microsoft.com/ko-kr/library/ms741870(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx


Windows Presentation Foundation (WPF) is designed to save developers from the difficulties of threading. As a result, the majority of WPF developers won't have to write an interface that uses more than one thread. Because multithreaded programs are complex and difficult to debug, they should be avoided when single-threaded solutions exist.

No matter how well architected, however, no UI framework will ever be able to provide a single-threaded solution for every sort of problem.  WPF comes close, but there are still situations where multiple threads improve user interface (UI) responsiveness or application performance. After discussing some background material, this paper explores some of these situations and then concludes with a discussion of some lower-level details.

System_CAPS_noteNote

This topic discusses threading by using the BeginInvoke method for asynchronous calls. You can also make asynchronous calls by calling theInvokeAsync method, which take an Action or Func<TResult> as a parameter. The InvokeAsync method returns a DispatcherOperation orDispatcherOperation<TResult>, which has a Task property. You can use the await keyword with either the DispatcherOperation or the associatedTask. If you need to wait synchronously for the Task that is returned by a DispatcherOperation or DispatcherOperation<TResult>, call theDispatcherOperationWait extension method. Calling Task.Wait will result in a deadlock. For more information about using a Task to perform asynchronous operations, see Task Parallelism. The Invoke method also has overloads that take an Action or Func<TResult> as a parameter. You can use the Invoke method to make synchronous calls by passing in a delegate, Action or Func<TResult>.

Overview and the Dispatcher

Typically, WPF applications start with two threads: one for handling rendering and another for managing the UI. The rendering thread effectively runs hidden in the background while the UI thread receives input, handles events, paints the screen, and runs application code. Most applications use a single UI thread, although in some situations it is best to use several. We’ll discuss this with an example later.

The UI thread queues work items inside an object called a Dispatcher. The Dispatcher selects work items on a priority basis and runs each one to completion.  Every UI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread.

The trick to building responsive, user-friendly applications is to maximize the Dispatcher throughput by keeping the work items small. This way items never get stale sitting in the Dispatcher queue waiting for processing. Any perceivable delay between input and response can frustrate a user.

How then are WPF applications supposed to handle big operations? What if your code involves a large calculation or needs to query a database on some remote server? Usually, the answer is to handle the big operation in a separate thread, leaving the UI thread free to tend to items in theDispatcher queue. When the big operation is complete, it can report its result back to the UI thread for display.

Historically, Windows allows UI elements to be accessed only by the thread that created them. This means that a background thread in charge of some long-running task cannot update a text box when it is finished.  Windows does this to ensure the integrity of UI components. A list box could look strange if its contents were updated by a background thread during painting.

WPF has a built-in mutual exclusion mechanism that enforces this coordination. Most classes in WPF derive from DispatcherObject. At construction, aDispatcherObject stores a reference to the Dispatcher linked to the currently running thread. In effect, the DispatcherObject associates with the thread that creates it. During program execution, a DispatcherObject can call its public VerifyAccess method.  VerifyAccess examines the Dispatcherassociated with the current thread and compares it to the Dispatcher reference stored during construction. If they don’t match, VerifyAccess throws an exception.  VerifyAccess is intended to be called at the beginning of every method belonging to a DispatcherObject.

If only one thread can modify the UI, how do background threads interact with the user? A background thread can ask the UI thread to perform an operation on its behalf. It does this by registering a work item with the Dispatcher of the UI thread. The Dispatcher class provides two methods for registering work items:  Invoke and BeginInvoke. Both methods schedule a delegate for execution.  Invoke is a synchronous call – that is, it doesn’t return until the UI thread actually finishes executing the delegate.  BeginInvoke is asynchronous and returns immediately.

The Dispatcher orders the elements in its queue by priority. There are ten levels that may be specified when adding an element to the Dispatcherqueue. These priorities are maintained in the DispatcherPriority enumeration. Detailed information about DispatcherPriority levels can be found in the Windows SDK documentation.

Threads in Action: The Samples

A Single-Threaded Application with a Long-Running Calculation

Most graphical user interfaces (GUIs) spend a large portion of their time idle while waiting for events that are generated in response to user interactions. With careful programming this idle time can be used constructively, without affecting the responsiveness of the UI. The WPF threading model doesn’t allow input to interrupt an operation happening in the UI thread. This means you must be sure to return to theDispatcher periodically to process pending input events before they get stale.

Consider the following example:

Prime numbers screen shot

This simple application counts upwards from three, searching for prime numbers. When the user clicks the Start button, the search begins. When the program finds a prime, it updates the user interface with its discovery. At any point, the user can stop the search.

Although simple enough, the prime number search could go on forever, which presents some difficulties. If we handled the entire search inside of the click event handler of the button, we would never give the UI thread a chance to handle other events. The UI would be unable to respond to input or process messages. It would never repaint and never respond to button clicks.

We could conduct the prime number search in a separate thread, but then we would need to deal with synchronization issues. With a single-threaded approach, we can directly update the label that lists the largest prime found.

If we break up the task of calculation into manageable chunks, we can periodically return to the Dispatcher and process events. We can give WPF an opportunity to repaint and process input.

The best way to split processing time between calculation and event handling is to manage calculation from the Dispatcher. By using theBeginInvoke method, we can schedule prime number checks in the same queue that UI events are drawn from. In our example, we schedule only a single prime number check at a time. After the prime number check is complete, we schedule the next check immediately. This check proceeds only after pending UI events have been handled.

Dispatcher queue illustration

Microsoft Word accomplishes spell checking using this mechanism. Spell checking is done in the background using the idle time of the UI thread. Let's take a look at the code.

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>

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;
    }
}

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));
    }
}

Besides updating the text on the Button, this handler is responsible for scheduling the first prime number check by adding a delegate to theDispatcher queue. Sometime after this event handler has completed its work, the Dispatcher will select this delegate for execution.

As we mentioned earlier, BeginInvoke is the Dispatcher member used to schedule a delegate for execution. In this case, we choose the SystemIdlepriority. The Dispatcher will execute this delegate only when there are no important events to process. UI responsiveness is more important than number checking. 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;

This method checks if the next odd number is prime. If it is prime, the method directly updates the bigPrimeTextBlock to reflect its discovery. We can do this because the calculation is occurring in the same thread that was used to create the component. 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 UI thread. We’ll demonstrate this situation next.

For the complete source code for this sample, see the Single-Threaded Application with Long-Running Calculation Sample

Handling a Blocking Operation with a Background Thread

Handling blocking operations in a graphical application can be difficult. We don’t want to call blocking methods from event handlers because the application will appear to freeze up. We can use a separate thread to handle these operations, but when we’re done, we have to synchronize with the UI thread because we can’t directly modify the GUI from our worker thread. We can use Invoke or BeginInvoke to insert delegates into theDispatcher of the UI thread. Eventually, these delegates will be executed with permission to modify UI elements.

In this example, we mimic a remote procedure call that retrieves a weather forecast. We use a separate worker thread to execute this call, and we schedule an update method in the Dispatcher of the UI thread when we’re finished.

Weather UI screen shot
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);
        }        
    }
}

The following are some of the details to be noted.

  • Creating 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);
    }
    

When the button is clicked, we display the clock drawing and start animating it. We disable the button. We invoke the FetchWeatherFromServermethod in a new thread, and then we return, allowing the Dispatcher to process events while we wait to collect the weather forecast.

  • Fetching 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);
    }
    

To keep things simple, we don’t actually have any networking code in this example. Instead, we simulate the delay of network access by putting our new thread to sleep for four seconds. In this time, the original UI thread is still running and responding to events. To show this, we’ve left an animation running, and the minimize and maximize buttons also continue to work.

When the delay is finished, and we’ve randomly selected our weather forecast, it’s time to report back to the UI thread. We do this by scheduling a call to UpdateUserInterface in the UI thread using that thread’s Dispatcher. We pass a string describing the weather to this scheduled method call.

  • Updating the UI

    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;     
    }
    

When the Dispatcher in the UI thread has time, it executes the scheduled call to UpdateUserInterface. This method stops the clock animation and chooses an image to describe the weather. It displays this image and restores the "fetch forecast" button.

Multiple Windows, Multiple Threads

Some WPF applications require multiple top-level windows. It is perfectly acceptable for one Thread/ Dispatcher combination to manage multiple windows, but sometimes several threads do a better job. This is especially true if there is any chance that one of the windows will monopolize the thread.

Windows Explorer works in this fashion. Each new Explorer window belongs to the original process, but it is created under the control of an independent thread.

By using a WPF Frame control, we can display Web pages. We can easily create a simple Internet Explorer substitute. We start with an important feature: the ability to open a new explorer window. When the user clicks the "new window" button, we launch a copy of our window in a separate thread. This way, long-running or blocking operations in one of the windows won’t lock all the other windows.

In reality, the Web browser model has its own complicated threading model. We’ve chosen it because it should be familiar to most readers.

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();
        }
    }
}

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();
}

This method is called when the "new window" button is clicked. It creates a new thread and starts it asynchronously.

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

This method is the starting point for the new thread. We create a new window under the control of this thread. WPF automatically creates a newDispatcher to manage the new thread. All we have to do to make the window functional is to start the  Dispatcher.

Technical Details and Stumbling Points

Writing Components Using Threading

The Microsoft .NET Framework Developer's Guide describes a pattern for how a component can expose asynchronous behavior to its clients (seeEvent-based Asynchronous Pattern Overview). For instance, suppose we wanted to package the FetchWeatherFromServer method into a reusable, nongraphical component. 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);

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.

One of the most important parts of this pattern is calling the MethodNameCompleted method on the same thread that called theMethodNameAsync method to begin with. You could do this using WPF fairly easily, by storing CurrentDispatcher—but then the nongraphical component could only be used in WPF applications, not in Windows Forms or ASP.NET programs. 

The DispatcherSynchronizationContext class addresses this need—think of it as a simplified version of Dispatcher that works with other UI 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();
}

Nested Pumping

Sometimes it is not feasible to completely lock up the UI thread. Let’s consider the Show method of the MessageBox class.  Show doesn’t return until the user clicks the OK button. It does, however, create a window that must have a message loop in order to be interactive. While we are waiting for the user to click OK, the original application window does not respond to user input. It does, however, continue to process paint messages. The original window redraws itself when covered and revealed. 

MessageBox with an "OK" button

Some thread must be in charge of the message box window. WPF 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). Instead, WPF uses a nested message processing system. The Dispatcher class includes a special method called PushFrame, which stores an application’s current execution point then begins a new message loop. When the nested message loop finishes, execution resumes after the original PushFrame call.

In this case, PushFrame maintains the program context at the call to MessageBoxShow, and it starts a new message loop to repaint the background window and handle input to the message box window. When the user clicks OK and clears the pop-up window, the nested loop exits and control resumes after the call to Show.

Stale Routed Events

The routed event system in WPF 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>

When the left mouse button is pressed over the ellipse, handler2 is executed. After handler2 finishes, the event is passed along to the Canvasobject, which uses handler1 to process it. This happens only if handler2 does not explicitly mark the event object as handled.

It’s possible that handler2 will take a great deal of time processing this event. handler2 might use PushFrame to begin a nested message loop that doesn’t return for hours. 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.

Reentrancy and Locking

The locking mechanism of the 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 actuality, the thread continues to receive and process high-priority messages. This helps prevent deadlocks and make interfaces minimally responsive, but it introduces the possibility for subtle bugs.  The vast majority of the time you don’t need to know anything about this, but under rare circumstances (usually involving Win32 window messages or COM STA components) this can be worth knowing.

Most interfaces are not built with thread safety in mind because developers work under the assumption that a UI is never accessed by more than one thread. In this case, that single thread may make environmental changes at unexpected times, causing those ill effects that theDispatcherObject mutual exclusion mechanism is supposed to solve. Consider the following pseudocode:

Threading reentrancy diagram

Most of the time that’s the right thing, but there are times in WPF where such unexpected reentrancy can really cause problems. So, at certain key times, WPF calls DisableProcessing, which changes the lock instruction for that thread to use the WPF reentrancy-free lock, instead of the usual CLR lock. 

So why did the CLR team choose this behavior? It had to do with COM STA objects and the finalization thread. When an object is garbage collected, its Finalize method is run on the dedicated finalizer thread, not the UI thread. And therein lies the problem, because a COM STA object that was created on the UI thread can only be disposed on the UI thread. The CLR does the equivalent of a BeginInvoke (in this case using Win32’sSendMessage). But if the UI thread is busy, the finalizer thread is stalled and the COM STA object can’t be disposed, which creates a serious memory leak. So the CLR team made the tough call to make locks work the way they do.  

The task for WPF is to avoid unexpected reentrancy without reintroducing the memory leak, which is why we don’t block reentrancy everywhere.




'프로그래밍 > WPF' 카테고리의 다른 글

Controls  (0) 2016.11.05
WPF Unmanaged API Reference  (0) 2016.11.05
Performance  (0) 2016.11.05
Migration and Interoperability  (0) 2016.11.05
Layout  (0) 2016.11.05
:
Posted by 지훈2
2016. 11. 5. 14:24

Performance 프로그래밍/WPF2016. 11. 5. 14:24

Performance



Graphics Rendering Tiers


https://msdn.microsoft.com/en-us/library/ms742196(v=vs.110).aspx


A rendering tier defines a level of graphics hardware capability and performance for a device that runs a WPF application.

Graphics Hardware

The features of the graphics hardware that most impact the rendering tier levels are:

  • Video RAM The amount of video memory on the graphics hardware determines the size and number of buffers that can be used for compositing graphics.

  • Pixel Shader A pixel shader is a graphics processing function that calculates effects on a per-pixel basis. Depending on the resolution of the displayed graphics, there could be several million pixels that need to be processed for each display frame.

  • Vertex Shader A vertex shader is a graphics processing function that performs mathematical operations on the vertex data of the object.

  • Multitexture Support Multitexture support refers to the ability to apply two or more distinct textures during a blending operation on a 3D graphics object. The degree of multitexture support is determined by the number of multitexture units on the graphics hardware.

Rendering Tier Definitions

The features of the graphics hardware determine the rendering capability of a WPF application. The WPF system defines three rendering tiers:

  • Rendering Tier 0 No graphics hardware acceleration. All graphics features use software acceleration. The DirectX version level is less than version 9.0.

  • Rendering Tier 1 Some graphics features use graphics hardware acceleration. The DirectX version level is greater than or equal to version 9.0.

  • Rendering Tier 2 Most graphics features use graphics hardware acceleration. The DirectX version level is greater than or equal to version 9.0.

The RenderCapability.Tier property allows you to retrieve the rendering tier at application run time. You use the rendering tier to determine whether the device supports certain hardware-accelerated graphics features. Your application can then take different code paths at run time depending on the rendering tier supported by the device.

Rendering Tier 0

A rendering tier value of 0 means that there is no graphics hardware acceleration available for the application on the device. At this tier level, you should assume that all graphics will be rendered by software with no hardware acceleration. This tier's functionality corresponds to a DirectX version that is less than 9.0.

Rendering Tier 1 and Rendering Tier 2

System_CAPS_noteNote

Starting in the .NET Framework 4, rendering tier 1 has been redefined to only include graphics hardware that supports DirectX 9.0 or greater. Graphics hardware that supports DirectX 7 or 8 is now defined as rendering tier 0.

A rendering tier value of 1 or 2 means that most of the graphics features of WPF will use hardware acceleration if the necessary system resources are available and have not been exhausted. This corresponds to a DirectX version that is greater than or equal to 9.0.

The following table shows the differences in graphics hardware requirements for rendering tier 1 and rendering tier 2:

Feature

Tier 1

Tier 2

DirectX version

Must be greater than or equal to 9.0.

Must be greater than or equal to 9.0.

Video RAM

Must be greater than or equal to 60MB.

Must be greater than or equal to 120MB.

Pixel shader

Version level must greater than or equal to 2.0.

Version level must greater than or equal to 2.0.

Vertex shader

No requirement.

Version level must greater than or equal to 2.0.

Multitexture units

No requirement.

Number of units must greater than or equal to 4.

The following features and capabilities are hardware accelerated for rendering tier 1 and rendering tier 2:

Feature

Notes

2D rendering

Most 2D rendering is supported.

3D rasterization

Most 3D rasterization is supported.

3D anisotropic filtering

WPF attempts to use anisotropic filtering when rendering 3D content. Anisotropic filtering refers to enhancing the image quality of textures on surfaces that are far away and steeply angled with respect to the camera.

3D MIP mapping

WPF attempts to use MIP mapping when rendering 3D content. MIP mapping improves the quality of texture rendering when a texture occupies a smaller field of view in a Viewport3D.

Radial gradients

While supported, avoid the use of RadialGradientBrush on large objects.

3D lighting calculations

WPF performs per-vertex lighting, which means that a light intensity must be calculated at each vertex for each material applied to a mesh.

Text rendering

Sub-pixel font rendering uses available pixel shaders on the graphics hardware.

The following features and capabilities are hardware accelerated only for rendering tier 2:

Feature

Notes

3D anti-aliasing

3D anti-aliasing is supported only on operating systems that support Windows Display Driver Model (WDDM), such as Windows Vista and Windows 7.

The following features and capabilities are not hardware accelerated:

Feature

Notes

Printed content

All printed content is rendered using the WPF software pipeline.

Rasterized content that uses RenderTargetBitmap

Any content rendered by using the Render method of RenderTargetBitmap.

Tiled content that uses TileBrush

Any tiled content in which the TileMode property of the TileBrush is set to Tile.

Surfaces that exceed the maximum texture size of the graphics hardware

For most graphics hardware, large surfaces are 2048x2048 or 4096x4096 pixels in size.

Any operation whose video RAM requirement exceeds the memory of the graphics hardware

You can monitor application video RAM usage by using the Perforator tool that is included in the WPF Performance Suite in the Windows SDK.

Layered windows

Layered windows allow WPF applications to render content to the screen in a non-rectangular window. On operating systems that support Windows Display Driver Model (WDDM), such as Windows Vista and Windows 7, layered windows are hardware accelerated. On other systems, such as Windows XP, layered windows are rendered by software with no hardware acceleration.

You can enable layered windows in WPF by setting the following Window properties:

Other Resources

The following resources can help you analyze the performance characteristics of your WPF application.

Graphics Rendering Registry Settings

WPF provides four registry settings for controlling WPF rendering:

Setting

Description

Disable Hardware Acceleration Option

Specifies whether hardware acceleration should be enabled.

Maximum Multisample Value

Specifies the degree of multisampling for antialiasing 3-D content.

Required Video Driver Date Setting

Specifies whether the system disables hardware acceleration for drivers released before November 2004.

Use Reference Rasterizer Option

Specifies whether WPF should use the reference rasterizer.

These settings can be accessed by any external configuration utility that knows how to reference the WPF registry settings. These settings can also be created or modified by accessing the values directly by using the Windows Registry Editor. For more information, see Graphics Rendering Registry Settings.

WPF Performance Profiling Tools

WPF provides a suite of performance profiling tools that allow you to analyze the run-time behavior of your application and determine the types of performance optimizations you can apply. The following table lists the performance profiling tools that are included in the Windows SDK tool, WPF Performance Suite:

Tool

Description

Perforator

Use for analyzing rendering behavior.

Visual Profiler

Use for profiling the use of WPF services, such as layout and event handling, by elements in the visual tree.

The WPF Performance Suite provides a rich, graphical view of performance data. For more information about WPF performance tools, see WPF Performance Suite.

DirectX Diagnostic Tool

The DirectX Diagnostic Tool, Dxdiag.exe, is designed to help you troubleshoot DirectX-related issues. The default installation folder for the DirectX Diagnostic Tool is:

~\Windows\System32

When you run the DirectX Diagnostic Tool, the main window contains a set of tabs that allow you to display and diagnose DirectX-related information. For example, the System tab provides system information about your computer and specifies the version of DirectX that is installed on your computer.

Screenhot: DirectX Diagnostic Tool

DirectX Diagnostic Tool main window












'프로그래밍 > WPF' 카테고리의 다른 글

WPF Unmanaged API Reference  (0) 2016.11.05
Threading Model  (0) 2016.11.05
Migration and Interoperability  (0) 2016.11.05
Layout  (0) 2016.11.05
Globalization and Localization  (0) 2016.11.05
:
Posted by 지훈2