04.05.2010 at
8:58 pm · Saved under
.NET Help
(The full set of ParallelExtensionsExtras Tour posts is available here.)
The Task Parallel Library provides the Task.Wait method, which synchronously waits for the target Task to complete. If the Task completed successfully, the method simply returns. If the Task completed due to an unhandled exception or cancellation, Wait throws an appropriate exception to connote that you can’t just blindly continue expecting the results or side-effects of the Task to have taken place.
While this is a very useful form of waiting, there are others that can be beneficial in certain situations, and ParallelExtensionsExtras includes a few different forms in the TaskExtrasExtensions.cs file.
WaitWithPumping
In a Windows Presentation Foundation application, especially when doing unit testing, you sometimes need to wait for a task on the UI thread. However, in doing so you don’t want to block the UI thread, and instead you want to continue the WPF message loop, maintaining a responsive application. For this purpose, ParallelExtensionsExtras includes the WaitWithPumping extension method for Task.
public static void WaitWithPumping(this Task task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new DispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
WaitWithPumping enters a message loop that will only exit when the task completes, which WPF knows about through a continuation applied to the task. Once the message loop has exited, we Wait on the task simply to propagate exceptions in the case where the task did not complete successfully.
With that in place, you could write WPF code like the following:
private void button1_Click(object sender, RoutedEventArgs e)
{
var t = Task.Factory.StartNew(() => Thread.Sleep(5000));
t.WaitWithPumping(); // UI remains responsive during call
MessageBox.Show(t.Status.ToString()); // will show “RanToCompletion”
}
Even thought Task t won’t complete for 5 seconds, and even though WaitWithPumping will block, the UI will still remain responsive. Then, because we waited for the Task to complete before exiting the message loop, the MessageBox will show “RanToCompletion”.
WaitForCompletionStatus
Sometimes you want to wait for a Task, but you don’t want the Wait operation to throw an exception, even if the target task completed in the Faulted or Canceled states. To achieve this, ParallelExtensionsExtras provides the WaitForCompletionStatus extension method on Task:
public static TaskStatus WaitForCompletionStatus(this Task task)
{
if (task == null) throw new ArgumentNullException(”task”);
((IAsyncResult)task).AsyncWaitHandle.WaitOne();
return task.Status;
}
This method relies on the fact that Task implements IAsyncResult, and thus implements the AsyncWaitHandle property. AsyncWaitHandle returns a WaitHandle that will be set when the task completes, and waiting on this wait handle will not throw exceptions in the same manner as does waiting on the task directly.
WaitForCompletionStatus returns the final TaskStatus of the task that was waited on. This makes it easy to write code that switches on a task’s completion state in order to do appropriate follow-up processing, e.g.
switch(task.WaitForCompletionStatus)
{
case TaskStatus.RanToCompletion:
Console.WriteLine(“Woo hoo!”);
break;
case TaskStatus.Faulted:
Console.WriteLine(“Uh oh: “ + task.Exception.Message);
break;
case TaskStatus.Canceled:
Console.WriteLine(“Oh well.”);
break;
}
Tagged: -net-4 • default-aspx • parallel-extensions • task- • task-parallel-library
23.04.2010 at
11:42 am · Saved under
.NET Help
(The full set of ParallelExtensionsExtras Tour posts is available here.)
Caches are ubiquitous in computing, serving as a staple of both hardware architecture and software development. In software, caches are often implemented as dictionaries, where some data is retrieved or computed based on a key, and then that key and its resulting data/value are added to the dictionary. Prior to re-retrieving or re-computing the value for a given key, we can first check the dictionary/cache to see whether we’ve already done so, and if we have, we simply copy the element from the dictionary.
As we all know, a multithreaded environment can bring with it many challenges, and such challenges apply to caches as well. Imagine creating a cache to store downloaded web pages. If multiple threads are trying to access the cache at the same time, we not only want to make sure that they don’t corrupt the employed data structures, we also want to make sure that they’re not doing more work than they need to: if two threads need the same page downloaded, just download it once rather than twice, and give them both copies. In this fashion, we need a form of an asynchronous cache that allows threads to get back a handle for the thing in the cache they want, a handle that, for example, will then provide them with a callback notification when the download has completed or that will allow them to wait for the download to complete.
The AsyncCache class in AsyncCache.cs in ParallelExtensionsExtras provides this support, and it may surprise you just how little code is required to do this, taking advantage of the new concurrency support in .NET 4. The type is defined as follows:
public class AsyncCache<TKey, TValue>
and contains two fields. The first field is a delegate that will be invoked for a key when that key is requested and is not yet in the dictionary; it is this delegate that produces the value for the key:
private readonly Func<TKey, Task<TValue>> _valueFactory;
Note that this isn’t a Func<TKey,TValue>, but rather a Func<TKey,Task<TValue>>. The function is supplied by the user to the AsyncCache constructor and produces a task that represents the retrieval of the value for a given key. This task could either be computational in nature (e.g. one created by Task.Factory.StartNew), or it could be async I/O-based, such as a task representing a download from a web site. Either way, it’s this task that’s stored in the cache in the second field:
private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;
As you might have guessed, we’re using a ConcurrentDictionary as the storage for the cache, which helps to ensure that multiple threads may access the cache concurrently without corrupting the internals of the data store. As noted earlier, the function to generate values for keys returns tasks, but the dictionary’s value isn’t just Task<TValue>, it’s Lazy<Task<TValue>>. The addition of the Lazy<> here makes it really easy to ensure that only one task is generated for any one key, avoiding any races that might otherwise result. We can see this by looking at the most important method on AsyncCache:
public Task<TValue> GetValue(TKey key)
{
var value = new Lazy<Task<TValue>>(() => _valueFactory(key));
return _map.GetOrAdd(key, value).Value;
}
You’ve now seen almost all of AsyncCache’s implementation… everything else in the type is really secondary (e.g. implementing the ICollection interface). GetValue simply creates a new Lazy<Task<TValue>> that will run the _valueFactory when invoked. The method then checks whether the dictionary already has a Lazy<> for this key, adding the one we just created if it didn’t yet have one, and regardless returning the Value of whatever Lazy<> we got back. By accessing the Lazy<Task<TValue>>’s Value, we get back the task for this key, and that’s handed back to the caller. The caller now has a Task<TValue> for the supplied TKey, and as with any other task, the caller can use ContinueWith to be notified when the task has completed, can Wait on the task to block until the task has completed, or can simply use its Result property to get at the data when it’s available (potentially blocking in the process).
With AsyncCache<TKey,TValue> in place, it’s now straightforward to either use it as is, or to create specialized variants of the cache. For example, in our earlier problem statement we described wanting to be able to cache downloaded web pages. Here’s the complete implementation of that:
public sealed class HtmlAsyncCache : AsyncCache<Uri, string>
{
public HtmlAsyncCache() :
base(uri => new WebClient().DownloadStringTask(uri)) { }
}
The DownloadStringTask extension method on WebClient is another method defined in ParallelExtensionExtras, and we’ll get to that another day. Suffice it to say that this method returns a Task<string> that represents the asynchronous downloading of a web page at the specified Uri. As such, our HtmlAsyncCache is simply a derived AsyncCache<Uri,string>, where the valueFactory calls DownloadStringTask for the supplied key/uri.
A consumer of this type may request a particular page:
HtmlAsyncCache cache = new HtmlAsyncCache();
…
Task<string> page =
cache.GetValue(new Uri(“http://www.microsoft.com”));
and then either use its value directly, blocking if it’s not yet available:
Console.WriteLine(page.Result);
or ask to be notified when the value is available:
page.ContinueWith(completed =>
Console.WriteLine(completed.Result));
And if you wanted to download multiple pages and only do something when you had all three, that’s easy as well. Since our asynchronous operations are represented as tasks, we can use the combinators provided by Task for this purpose, e.g.
var page1 = cache.GetValue(
new Uri(“http://msdn.microsoft.com/pfxteam”));
var page2 = cache.GetValue(
new Uri(“http://msdn.com/concurrency”));
var page3 = cache.GetValue(
new Uri(“http://www.microsoft.com”));
Task.Factory.ContinueWhenAll(
new [] { page1, page2, page3 }, completedPages =>
{
… // use the downloaded pages here
});
(Thanks go to Luca Bolognese for originally supplying the idea for AsyncCache.)
Tagged: -net-4 • cds • default-aspx • parallel-extensions • task-
09.07.2009 at
9:37 pm · Saved under
.NET Help
Parallel Extensions offers a large variety of APIs supporting parallelism.
During this blog the discussion will be focused on the methodology for making a choice between two of the new Parallel Extensions concepts: parallelism achieved by using Parallel.Invoke() and parallelism achieved through the use of Tasks.
Suppose that you wanted the two actions below to be executed in parallel:
Action hello = () => { Console.Write(”Hello”); };
Action world = () => { Console.Write(”World”); };
Should you use Tasks or Parallel.Invoke() to achieve the desired parallelism?
Parallelism through Parallel.Invoke()
The code to execute our actions in parallel with Parallel.Invoke() looks like this:
Parallel.Invoke(hello, world);
This is simple and easy, and we get the desired result: either “HelloWorld” or “WorldHello” output to the console, depending on the order in which the parallel actions were scheduled.
Parallelism through Explicit Task Management
For convenience, creating and starting a Task can be done with a single method:
Task.Factory.StartNew(hello);
Task.Factory.StartNew(world);
If we run this code sequence, something surprising happens – nothing is displayed! The reason is that the Tasks are scheduled via ThreadPool threads, which are background threads. The main process will execute while the Tasks are still running. At the same time, if one of the actions throws an exception, the exception will not be thrown until the Finalizer throws it. We can solve both of these problems by performing an explicit Wait() on the Tasks:
Task taskHello = Task.Factory.StartNew(hello);
Task taskWorld = Task.Factory.StartNew(world);
Task.WaitAll(taskHello, taskWorld);
Running this sample now, the result is the same as in the Parallel.Invoke case: either “HelloWorld” or “WorldHello” is printed to the console.
An Example: Tree Traversal
Let’s see now how we can implement a binary tree traversal using Parallel.Invoke():
ConcurrentBag<int> _dataStorage = new ConcurrentBag<int>();
void TreeTraversal(Tree<int> node)
{
if (node == null)
return;
var actions = new List<Action>();
if(node._left != null)
actions.Add(() => TreeTraversal(node._left));
if(node._right != null)
actions.Add(() => TreeTraversal(node._right));
Parallel.Invoke(actions.ToArray());
_dataStorage.Add(node._data);
}
And let’s look at a similar version using explicit Task management:
ConcurrentBag<int> _dataStorage = new ConcurrentBag<int>();
void TreeTraversal(Tree<int> node)
{
if (node == null)
return;
var tasks = new List<Task>();
if(node._left != null)
tasks.Add(Task.Factory.StartNew(
() => TreeTraversal(node._left));
if(node._right != null)
tasks.Add(Task.Factory.StartNew(
() => TreeTraversal(node._right));
_dataStorage.Add(node._data);
Task.WaitAll(tasks.ToArray());
}
Note that the syntax is more concise in the Parallel.Invoke() version, and the Parallel.Invoke() call takes care of waiting and exception handling. The flip side of that is that the explicit task management version allows for more control. Note that the “_dataStorage.Add(node._data);” operation can be performed in parallel with the processing of the left and right branches when explicit task management is used, while the same operation must wait for left/right processing to complete in the Parallel.Invoke() version.
Conclusions
Parallel.Invoke() is a higher-level mechanism for providing parallelism, and allows for more concise code that one would typically get from using explicit task management.
However, if the coder is interested in more control, perhaps for more complicated scenarios, then explicit task management is probably the way to go.
Tagged: actions • cristina-manu • management • media • programming • silverlight • task- • wikipedia