Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Applications and Their Threading Models

The .NET Framework supports several different kinds of application models, and each application model can impose its own threading model. Console applications and Windows Services (which are really console applications; you just don’t see the console) do not impose any kind of threading model; that is, any thread can do whatever it wants when it wants.

However, GUI applications, including Windows Forms, Windows Presentation Foundation (WPF), Silverlight, and Windows Store apps impose a threading model where the thread that created a UI element is the only thread allowed to update that UI element. It is common for the GUI thread to spawn off an asynchronous operation so that the GUI thread doesn’t block and stop responding to user input like mouse, keystroke, pen, and touch events. However, when the asynchronous operation completes, a thread pool thread completes the Task object resuming the state machine.

For some application models, this is OK and even desired because it’s efficient. But for some other application models, like GUI applications, this is a problem, because your code will throw an exception if it tries to update UI elements via a thread pool thread. Somehow, the thread pool thread must have the GUI thread update the UI elements.

ASP.NET applications allow any thread to do whatever it wants. When a thread pool thread starts to process a client’s request, it can assume the client’s culture (System.Globalization.Culture­ Info), allowing the web server to return culture-specific formatting for numbers, dates, and times.11 In addition, the web server can assume the client’s identity (System.Security.Principal.

IPrincipal), so that the server can access only the resources that the client is allowed to access. When a thread pool thread spawns an asynchronous operation, it may be completed by another thread pool thread, which will be processing the result of an asynchronous operation. While this

 

 
 

11 For more information, see http://msdn.microsoft.com/en-us/library/bz9tc508.aspx.


work is being performed on behalf of the original client request, the culture and identity needs to “flow” to the new thread pool thread so any additional work done on behalf of the client is per- formed using the client’s culture and identity information.

Fortunately, the FCL defines a base class, called System.Threading.SynchronizationContext, which solves all these problems. Simply stated, a SynchronizationContext-derived object con- nects an application model to its threading model. The FCL defines several classes derived from SynchronizationContext, but usually you will not deal directly with these classes; in fact, many of them are not publicly exposed or documented.

For the most part, application developers do not need to know anything about the Synchroni­ zationContext class. When you await a Task, the calling thread’s SynchronizationContext object is obtained. When a thread pool thread completes the Task, the SynchronizationContext object is used, ensuring the right threading model for your application model. So, when a GUI thread awaits a Task, the code following the await operator is guaranteed to execute on the GUI thread as well, allowing that code to update UI elements.12 For an ASP.NET application, the code following the await operator is guaranteed to execute on a thread pool thread that has the client’s culture and principal information associated with it.



Most of the time, having a state machine resume using the application model’s threading model is phenomenally useful and convenient. But, on some occasions, this can get you into trouble. Here is an example that causes a WPF application to deadlock.

 

private sealed class MyWpfWindow : Window { public MyWpfWindow() { Title = "WPF Window"; }

 

protected override void OnActivated(EventArgs e) {

// Querying the Result property prevents the GUI thread from returning;

// the thread blocks waiting for the result

String http = GetHttp().Result; // Get the string synchronously!

 

base.OnActivated(e);

}

 

private async Task<String> GetHttp() {

// Issue the HTTP request and let the thread return from GetHttp

HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/");

// We never get here: The GUI thread is waiting for this method to finish but this method

// can't finish because the GUI thread is waiting for it to finish ­­> DEADLOCK!

 

return await msg.Content.ReadAsStringAsync();

}

}

 

Developers creating class libraries definitely need to be aware of the SynchronizationContext class so they can write high-performance code that works with all application models. Because a lot of class library code is application model agnostic, we want to avoid the additional overhead involved

 
 

12 Internally, the various SynchronizationContext-derived classes get the GUI thread to resume the state machine using methods like System.Windows.Forms.Control.BeginInvoke, System.Windows.Threading.Dispatcher. BeginInvoke, and Windows.UI.Core.CoreDispatcher.RunAsync.


in using a SynchronizationContext object. In addition, class library developers should do every- thing in their power to help application developers avoid deadlock situations. To solve both of these problems, both the Task and Task<TResult> classes offer a method called ConfigureAwait whose signature looks like this.

 

// Task defines this method:

public ConfiguredTaskAwaitable ConfigureAwait(Boolean continueOnCapturedContext);

 

// Task<TResult> defines this method:

public ConfiguredTaskAwaitable<TResult> ConfigureAwait(Boolean continueOnCapturedContext);

 

Passing true to this method gives you the same behavior as not calling the method at all. But, if you pass false, the await operator does not query the calling thread’s SynchronizationContext object and, when a thread pool thread completes the Task, it simply completes it and the code after the await operator executes via the thread pool thread.

Even though my GetHttp method is not class library code, the deadlock problem goes away if I add calls to ConfigureAwait. Here is the modified version of my GetHttp method.

 

private async Task<String> GetHttp() {

// Issue the HTTP request and let the thread return from GetHttp HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/")

.ConfigureAwait(false);

// We DO get here now because a thread pool can execute this code

// as opposed to forcing the GUI thread to execute it.

 

return await msg.Content.ReadAsStringAsync().ConfigureAwait(false);

}

 

As the preceding code shows, ConfigureAwait(false) must be applied to every Task object you await. This is because asynchronous operations may complete synchronously and, when this happens, the calling thread simply continues executing without returning to its caller; you never know which operation requires ignoring the SynchronizationContext object, so you have to tell all of them to ignore it. This also means that your class library code should be application model agnostic.

Alternatively, I could re-write my GetHttp method as follows so that the whole thing executes via a thread pool thread.

 

private Task<String> GetHttp() { return Task.Run(async () => {

// We run on a thread pool thread now that has no SynchronizationContext on it HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/");

// We DO get here because some thread pool can execute this code

 

return await msg.Content.ReadAsStringAsync();

});

}

 

In this version of the code, notice that my GetHttp method is not an async function; I removed the async keyword from the method signature, because the method no longer has an await operator in it. On the other hand, the lambda expression I pass to Task.Run is an async function.



Date: 2016-03-03; view: 876


<== previous page | next page ==>
Nbsp;   Other Async Function Features | Nbsp;   Some I/O Operations Must Be Done Synchronously
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)