The document discusses concurrency and asynchronous programming in .NET. It covers three pillars of concurrency: scalability using Parallel.For, responsiveness using tasks and async/await, and consistency using locks, mutexes, etc. It demonstrates how async/await allows programs to be responsive by freeing threads for other work while waiting for I/O. A key point is that the SynchronizationContext impacts asynchronous code behavior and can cause unintended threading issues if not properly understood.
10. string ReadSomeText (string fileName)
{
using (var sr = new StreamReader(fileName))
{
var result = sr.ReadToEnd ();
return result;
}
}
// Blocks the thread until IO completes
var text = ReadSomeText ("SomeText.txt");
15. async Task<string> ReadSomeTextAsync(string fileName)
{
using (var sr = new StreamReader(fileName))
{
var first = await sr.ReadLineAsync();
var second = await sr.ReadLineAsync();
var third = await sr.ReadLineAsync();
return first + second + third;
}
}
16. async Task<string> ReadSomeTextAsync(string fileName)
{
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
return result;
}
}
// Is this code correct?
var readTask = ReadSomeTextAsync ("SomeText.txt");
var text = readTask.Result;
18. // Is this code correct (ignore exceptions)?
async Task<string> ReadSomeTextAsync(string fileName)
{
++m_readingFiles;
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
--m_readingFiles;
return result;
}
}
20. // Does both TraceTheadId() trace the same id?
async Task<string> ReadSomeTextAsync(string fileName)
{
TraceThreadId ();
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
TraceThreadId ();
return result;
}
}
23. Your new ”best” friends
SynchronizationContext
Console apps
Continues on ThreadPool thread
WindowsFormsSynchronizationContext
Used by WindowsForms apps
Continues on the ”UI” thread
DispatcherSynchronizationContext
Used by WPF and ASP.NET apps
Continues on the ”same” thread
25. async Task<string> ReadSomeTextAsync(string fileName)
{
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
return result;
}
}
// Is this code correct?
var readTask = ReadSomeTextAsync ("SomeText.txt");
var text = readTask.Result;
27. // Is this code correct (ignore exceptions)?
async Task<string> ReadSomeTextAsync(string fileName)
{
++m_readingFiles;
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
--m_readingFiles;
return result;
}
}
29. // Does both TraceTheadId() trace the same id?
async Task<string> ReadSomeTextAsync(string fileName)
{
TraceThreadId ();
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
TraceThreadId ();
return result;
}
}
32. async Task<string> ReadSomeTextAsync(string fileName)
{
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
return result;
}
}
// Is this code correct?
var readTask = ReadSomeTextAsync ("SomeText.txt");
var text = readTask.Result;
34. // Is this code correct (ignore exceptions)?
async Task<string> ReadSomeTextAsync(string fileName)
{
++m_readingFiles;
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
--m_readingFiles;
return result;
}
}
36. // Does both TraceTheadId() trace the same id?
async Task<string> ReadSomeTextAsync(string fileName)
{
TraceThreadId ();
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync();
TraceThreadId ();
return result;
}
}
39. SynchronizationContext
Is ”invisible”
Is a thread-global state
Impacts the behavior of your code significantly
As an application developer you can make assumptions
As a library developer you can’t make assumptions
41. async Task<string> ReadSomeTextAsync(string fileName)
{
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync()
.ConfigureAwait (false);
return result;
}
}
// Is this code correct?
var readTask = ReadSomeTextAsync ("SomeText.txt");
var text = readTask.Result;
43. // Is this code correct (ignore exceptions)?
async Task<string> ReadSomeTextAsync(string fileName)
{
++m_readingFiles;
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync()
.ConfigureAwait (false);
--m_readingFiles;
return result;
}
}
45. // Does both TraceTheadId() trace the same id?
async Task<string> ReadSomeTextAsync(string fileName)
{
TraceThreadId ();
using (var sr = new StreamReader(fileName))
{
var result = await sr.ReadToEndAsync()
.ConfigureAwait (false);
TraceThreadId ();
return result;
}
}
49. What we need
Do one thing
Responsiveness
Predictable semantics
Continuation is executed by a thread-pool thread
Visibility
Thread-switching should be visible in code
50. F# async
// Focuses on responsiveness
let gameLoop =
async {
// Switching to new thread is explicit
do! Async.SwitchToNewThread ()
while true do
// let! is like await in C#
let! messages = fromVisual.AsyncDequeue 1000
for message in messages do
processMessage message
}
51. C#
yield
Special support for enumerators
LINQ
Special support for SQL-like syntax
async/await
Special support for asynchronous programming
52. F#
seq { for x in 0..10 -> i }
Enumerators implemented using Computation Expressions
query { for c in db.Customers do select c }
SQL-like syntax implemented using Computation Expressions
async { let! r=fromVisual.AsyncDequeue 1000 in r }
Asynchronous programming using Computation Expressions