TechDays2013 NL session on async and parallel programming. Gives an overview of todays relevant .net technologies, examples and tips and tricks. This session will help you to understand and select and use the right async/parallel technology to use in your .net application. (arie@macaw.nl)
4. Objectives
⢠Understanding Async and Parallelism
⢠Patterns, Frameworks and tools
â Purpose
â Scenarios
â Combinations and Integrations
â Real world examples
5. Why Async and Parallel?
⢠Multicore is the rule
â Keep your cores busy
â Used them in an efficient way
⢠Client
â User experience
⢠Server
â Scalability
â Handling requests
⢠Real life is async and parallel
6. Question
⢠Whatâs the difference between Async
and Parallel?
⢠Parallel : Having the work done by multiple
workers at the same time
⢠Async: Use workers efficiently, donât let them
wait for slow actions to complete
7. History
⢠.NET 1.0
â Asynchronous Programming Model, APM
⢠.NET 2.0
â Event-Based Programming Model, EPM
⢠.NET 4.0
â Task-based Programming Model, TAP
⢠.NET 4.5
â C# 5.0, async, await(*)
(*) Async Targeting Pack for Visual Studio 2012 to target .NET 4.0
10. Event-based Programming
Model = new WebClient();
var client
client.DownloadStringCompleted
+= DownloadCompleted;
private void DownloadCompleted(
object sender,
DownloadStringCompletedEventArgs e) {
string resp = e.Result;
}
client.DownloadStringAsync(uri);
11. Event-based Programming
Model and EventHandlers
⢠Events
⢠Complex code
â Error handling
â Inconsistent synchronization context
⢠Possible Memory leaks
â += handler keeps reference
â Dangling handlers
12. Tasks
⢠Task and Task<T>
â A promise to do something or return a value
â Does not say when or how
⢠Task != Thread
â A task is something you want to be done
â A thread is a possible worker to perform that task
â A task may not even need a thread
13. Why use Tasks
⢠Simplifies code
⢠One object to access to get the
â Result, Status and possible Errors
⢠Composition
â ContinueWith, WhenAll, WhenAny
⢠Task Cancelation
14. Creating a Task
// Using the Task constructor
var task1 = new Task<int>(() => {
Thread.Sleep(1000);
return 42;
}); This is the work to be done
task1.Start();
var res = task1.Result;
// Using the TaskFactory
var task2 = new TaskFactory<int>().StartNew(() => {
Thread.Sleep(1000);
return 42;
});
15. TaskScheduler
⢠Handles low level queuing of Tasks to Threads
⢠Uses lightweight TreadPool
â Number of cores
â Default scheduler proviced by .NET
â TaskScheduler.FromCurrentSynchronizationContext()
⢠Can specify alternative scheduler for task to run
on
â Task.Start()
16. TaskCreationOptions
⢠Task creation options to hint
TaskScheduler
â AttachedToParent
â HideScheduler
â LongRunning
â PreferFairness
â A few more..
17. Task Scheduler
LongRunning
PreferFairness
Work Stealing Task option
Local Queue Task option
Global Queue (FIFO)
Run created by work outside
Task on standalone taskif task in
Idle threads by tasks placed in
Taskscreatedstealrunning no tasks in
Instead global
global queue Queue
local Queue (LIFO)
Local or of threadpool
Threadpool
Worker Worker Worker Worker
Thread 1 Thread 2 Thread 3 Thread n
= Running Task
= Queued Task
18. Outer task is executed by
Using PreferFairness thread from threadpool
Task<int> outer = Task<int>.Factory.StartNew( () => {
Creates 2 more inner
var cts = new CancellationTokenSource(); tasks, queued in local
var token = cts.Token; task LIFO queue
var tasks = new Task<int>[]{
Task.Factory.StartNew(() => { methodA(token); }, token),
Task.Factory.StartNew(() => { { methodB(token); }, token)
Task.Factory.StartNew(() => methodA(token); }, token,
}; TaskCreationOptions.PreferFairness),
Task.Factory.StartNew(() => { methodB(token); }, token),
var winnerIndex = Task.WaitAny(tasks);
TaskCreationOptions.PreferFairness),,
cts.Cancel();
On a busy system one of
return tasks[winnerIndex].Result;
});
the tasks might never
return outer.Result; execute (no workstealing
by other thread)
19. ConcurrentExclusiveSchedulerPair
⢠Provides task schedulers that coordinate
to execute tasks
⢠Scheduler properties
â ConcurrentScheduler
⢠Schedule tasks to this pair that may run
concurrently with other tasks on this pair
â ExclusiveScheduler
⢠Schedule tasks to this pair that must run
exclusively with regards to other tasks on this pair
20. SchedulerPairs
var schedPair =
new ConcurrentExclusiveSchedulerPair();
// Tasks on this scheduler may run concurrently
readerTask.Start(schedPair.ConcurrentScheduler);
// Tasks on this scheduler run exclusivly
writerTask.Start(schedPair.ExclusiveScheduler);
21. How to Cancel a Task
⢠Tasks does not have Stop Method
⢠CancelationTokenSource
⢠-Create CancelationToken and pass to Task
â Signal a Task to Stop
â Task should check token for cancelation
â Return or Throw Exception
⢠Additional options for timed cancellations etc.
⢠Can also be used for (timed) Wait actions
22. Cancelling a Task
var tokenSource2 = new CancellationTokenSource();
var ct = tokenSource2.Token;
var task = Task.Factory.StartNew(() => {
while (true) {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
// Do Work
}
}, ct );
tokenSource2.Cancel();
23. Error and Exception Handling
⢠Exceptions occurred in execution of
task thrown when accessing the Result
⢠System.AggregateException
â InnerExceptions
try {
var result = task.Result;
}
catch( AggregateException ex )
{
foreach (var e in ex.InnerExceptions){
// Exception handling here
}
25. Stopping Parallel Loops
var cancellationSource = new CancellationTokenSource(); Using C#
var options = new ParallelOptions(); Break
options.CancellationToken = cancellationSource.Token;
Using
Parallel.For(0, 10, options, (a, loopState) => loopstate
{
. . . .
// cancellationToken.Cancel can be called externally
// or Break can be called directly if condition is true
if (cancellationToken.IsCancellationRequested)
loopState.Break(); // loopState.Stop();
. . . .
}); Throws OperationCanceledException
26. Parallel Options for Loops
⢠CancelationToken
â Stopping all Threads in the loop
⢠MaxDegreeOfParallelism
â If you need more or less Threads
⢠TaskScheduler
27. PLINQ
var query =
(from d in data
where âŚ
select d).AsParallel();
query.ForAll((d) => DoWork(d));
29. ConcurrentDictionary
var dict = new ConcurrentDictionary<string, string>();
var key = "key01";
var value = "value01";
// Get a value
dict.GetOrAdd(key, k => GetValue(key));
// Update or add a value
value = "newvalue";
dict.AddOrUpdate(key, value, (k, o) => value);
30. C# 5.0
⢠New keywords for async
async marks method or lambda
expression as async
await suspends execution of method until
the awaited tasks returns
⢠Compiler does generate all the code
to make this happen
31. C# 5.0
public int LongRunningMethod()
{
Task.Delay(1000); Task to return
Wait for
var result = Control 2;returned to caller
21 * is
return result;
}
public async Task<int> LongRunningMethodAsync()
{
await Task.Delay(1000);
var result = 21 * 2;
return result; This code runs when Task returns
}
32. Run
The story of two Tasks (1)
await DoAsync("Task-1");
await DoAsync("Task-2");
Async but not parallel
33. Run
The story of two Tasks (2)
var t1 = DoAsync("Task-1");
var t2 = DoAsync("Task-2");
Parallel but returns immediately
34. Run
The story of two Tasks (3)
var t1 = DoAsync("Task-1");
var t2 = DoAsync("Task-2");
await t1;
await t2;
Parallel and wait for both tasks to return
35. The story of two Tasks (4)
var t1 = DoAsync("Task-1");
var t2 = DoAsync("Task-2");
await Task.WhenAll(t1,t2)
Parallel and wait for both tasks to return
But more efficient
36. Where await canât be used
⢠catch and finally blocks
⢠lock blocks
⢠LINQ query expressions
â Allowed in extension methods (. syntax)
⢠Unsafe code
37. How does await work
⢠Does the C# compiler depends on .NET Task class
for await keyword ?
⢠Compiler needs GetAwaiter() method
⢠Method must return a class that implements
â GetResult()
â IsCompleted()
â INotifyCompleted.OnCompleted
⢠Implemented in Task
38. Demo
⢠Implementing an awaitable class
â See what the compiler needs for await
â Just for demo purposes
39. Make something async using
Task
⢠Simply create a new Task to do the work
public static async Task<int> MyAsync()
{
var result = await new Task<int>(() =>
{
// your long running code code here...
var calculatedResult = LongCalculation();
return calculatedResult;
});
return result;
}
40. Tasks without Threads
⢠Task construction always take code to
execute on threadpool
⢠But what if youâŚ
â Already have a thread
â Donât need a thread
â Waiting for async IO, network etc
â Handle events
43. Task Interop patterns
⢠Why
â Unified Task-based programming model
â Composition
⢠Asynchronous to Task-based application model
â Factory.TaskFromAsync
⢠Event-basedto Task-based application model
â TaskCompletionSource
44. Async to Task-based
WebRequest wr =
WebRequest.CreateHttp(url);
var t = Task<Stream>.Factory.FromAsync(
wr.BeginGetRequestStream,
wr.EndGetRequestStream,
null
);
45. Event to Tasks-based (1)
public static Task<string> DownloadStringAsync(Uri url){
var wc = new WebClient();
var tcs = new TaskCompletionSource<string>();
wc.DownloadStringCompleted += (s,e) =>{
if (e.Error != null ) tcs.TrySetException(e.Error);
else if (e.Cancelled) tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result)
}
Task complete
wc.DownloadStringAsync(url);
return tcs.Task;
}
46. Event to Task-based (2)
private Task<string> LoadDocumentAsync(
WebBrowser browser, string url)
{
var tcs = new TaskCompletionSource<string>();
WebBrowserDocumentCompletedEventHandler handler = null;
Event source lifetime
handler = (sender, managed externally
e) =>
{
tcs.SetResult(browser.DocumentText);
browser.DocumentCompleted -= handler;
};
browser.DocumentCompleted += handler;
browser.Url = new Uri(url); Need to unregister
return tcs.Task; handler
}
47. Using Lazy<T> with async
⢠Lazy<T> is thread safe and supports async
// sync version
async version
var lazy = new=Lazy<string>(() =>
lazyAsync new Lazy<Task<string>>(async () => {
{
Thread.Sleep(5000);
await Task.Delay(5000); Factory code
return DateTime.Now.ToString();
});
});
var value = lazy.Value;
var value = await lazyAsync.Value;
48. Using Lazy<T> with async
var lazyAsync = new Lazy<Task<string>>(async () =>
{
Console.WriteLine("I'm only called once...");
await Task.Delay(5000);
return DateTime.Now.ToString();
});
// Starts and wait for task
var t1 = lazyAsync.Value;
// Waits for same task
var t2 = lazyAsync.Value;
49. Building an async cache
public class AsyncCache<TKey, TValue>{
private Func<TKey,Task<TValue>> factory;
private ConcurrentDictionary<TKey,Lazy<Task<TValue>>> dict;
var cache = new AsyncCache<string, string>( async k =>
public AsyncCache(Func<TKey, Task<TValue>> valueFactory){
{
factory = valueFactory;
dictwait Task.Delay(1000);
= new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
}
return DateTime.Now.ToString();
});
public Task<TValue> this[TKey key] {
get {
return dict.GetOrAdd(key,
var t = await cache["key1"];
toAdd => new Lazy<Task<TValue>>(() => factory(toAdd))).Value; }
}
}
50. SynchronizationContext
⢠Used when code needs specific context
â e.g. updating UI
â SynchronizationContext class is abstract
â Static Current property returns relevant
instance (Post is specific)
Ensures one at a time Dispatcher.BeginInvoke Control.BeginInvoke
51. SynchronizationContext
private void Button1Click(object sender,EventArgs e)
{
var ctx = SynchronizationContext.Current;
new TaskFactory().StartNew(() => {
ctx.Post(state => {
label1.Text = string.Format("Update from Task")
}, null});
}
⢠await Task continues automatically on
Current context
52. Windows8
⢠WinRT is based on COM
⢠Async based on interfaces like this
â IAsyncAction
â IAsyncOperation<T>
⢠Task nor CLR do not implement these
interfaces
53. Win8
⢠How can we call these new asynch methods from
.NET or use Tasks in Win8 applications?
⢠WindowsRuntimeSystemExtensions methods take
care of conversions
public static Task AsTask<TResult>(
this IAsyncOperation<TResult> source);
public static IAsyncOperation<TResult>
AsAsyncOperation<TResult>(this Task<TResult> source);
54. Async and ASP.NET
⢠Async controller methods
â Donât block your IIS worker threads
â Avoid 503 (Server too busy) errors
⢠MVC4/.NET4.5 supports async/await
â Possible in MVC3, but more complex
â WebForms also have support for async
⢠Always use async proxies when calling
external services
55. Async in MVC3
public class MVC3Controller : AsyncController{
public void IndexAsync(){
AsyncManager.OutstandingOperations.Increment(1);
Task.Factory.StartNew(() =>{
var client = new WebClient();
string reply = client.DownloadString(uri);
AsyncManager.Parameters["data"] = reply;
AsyncManager.OutstandingOperations.Decrement();});
}
public ActionResult IndexCompleted(string data){
this.ViewBag.Result = data;
return View();
}
}
56. Async in MVC4
public class MVC4Controller : Controller
{
public async Task<ActionResult> Index() {
var client = new WebClient();
this.ViewBag.Result =
await client.DownloadStringTaskAsync(uri);
return View();
}
}
58. Parallel programming models
⢠.NET 4.0
â Hereâs the data, now setup computing
â Primitives for Tasks and Data parallelism
⢠The Inverse model
â Setup the Computing, now hereâs the data
â Primitives for Dataflow parallelism
60. Reactive Extensions and TPL
Dataflow
⢠Reactive Extension (Rx)
â Coordination and composition of event streams
â LINQ-based API
⢠Dataflow (TDF)
â Building blocks for message passing and parallelizing
â Explicit control over how data is buffered and moved
⢠Many similarities, but each address distinct needs
61. Reactive Extensions Rx
⢠Library for composing asynchronous
and event-based programs using
observable sequences and LINQ-style
query operators.
â Rx for .NET, Rx.NET
â Rx for JavaScript, RxJS
â Rx for Windows Phone
62. Reactive Extensions
var observable1 = Observable.Range(1, 20);
var subscription1 = observable1.Subscribe<int>(Console.WriteLine);
var oneSecond = TimeSpan.FromSeconds(1);
var observable2 = Observable.Timer(oneSecond,oneSecond);
var subscription2 = observable2.Subscribe<int>(Console.WriteLine);
var observer3 = observable1
.Select((i) => i)
.Skip(2) // skip first two values
.Where(i => (i % 2 == 0)) // only get the even values
.Zip(observable2, (i, t) => i) // one value per second
.Subscribe((i) => Console.WriteLine(i));
63. Reactive Extensions
var mouseMove = Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => this.MouseMove += h, h => this.MouseMove -= h);
var mouseUp = Observable
.FromEvent<MouseEventHandler, MouseEventArgs>(
h => this.MouseUp += h, h => this.MouseUp -= h);
var mouseDown = Observable
.FromEvent<MouseEventHandler, MouseEventArgs>(
h => this.MouseDown += h, h => this.MouseDown -= h);
var observable = mouseMove // Get mousemove positions
.SkipUntil(mouseDown) // Skip until mouse button down
.TakeUntil(mouseUp) // Take until mouse button is up
.Select(a => a.EventArgs.Location);
64. Reactive Extensions
⢠Many operators
â Skip, Take, Zip, Throttle, Buffer, RepeatâŚ
⢠ObserveOn() and SubscribeOn()
methods
â Optional Scheduler and Context
parameters
â Specify which thread/context the observer
and subscribers run on
65. TPL Dataflow
⢠Primitives for in-process message/data
passing
â Blocks for buffering and processing data
⢠Linkable to form a network
â Data automatically propagated from sources to
linked targets
â Enables building powerful parallel and
asynchronous pipelinesBased
⢠Integrates with Task, IObservable,âŚ
69. Building a Dataflow network
transform1.LinkTo(join.Target1);
transform2.LinkTo(join.Target2);
input.LinkTo(action1); join.LinkTo(action2);
input.LinkTo(transform1);
input.LinkTo(transform2);
70. ActionBlock in action
var actionBlock = new ActionBlock<int>((i) =>
{
Console.WriteLine("[{0}]t{1}",
Thread.CurrentThread.ManagedThreadId,i);
}
);
for (var i = 0; i < 10; i++)
actionBlock.Post(i);
71. ActionBlock in action
var actionBlock = new ActionBlock<int>((i) =>
{
Console.WriteLine("[{0}]t{1}",
Thread.CurrentThread.ManagedThreadId,i);
},
new ExecutionDataflowBlockOptions() {
MaxDegreeOfParallelism = 4 }
);
Max 4 instances
for (var i = 0; i < 10; i++) running
actionBlock.Post(i);
72. Linking Blocks
var actionBlock = new ActionBlock<int>((i) => Console.WriteLine(i));
var transformBlock = new TransformBlock<int, int>((i) => i * i);
transformBlock.LinkTo(actionBlock);
for (var i = 0; i < 10; i++)
transformBlock.Post(i);
73. Buffering
var actionBlock = new ActionBlock<int>((i) => Console.WriteLine(i));
var bufferBlock = new BufferBlock<int>(new DataflowBlockOptions(
{ BoundedCapacity = 10 }
);
Post blocks if buffer is full
bufferBlock.LinkTo(actionBlock);
74. How about JavaScript?
⢠Use async in the browser
â Reactive Extensions for JavaScript
â jQuery Defered and Promises