using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
/*
Name Description Exceptions
====================================================================================================================
Avoid async void Prefer async Task methods over async void methods Event handlers
Async all the way Don’t mix blocking and async code Console main method
Configure context Use ConfigureAwait(false) when you can Methods that require context
To Do This Instead of This Use This
====================================================================================================================
Retrieve the result of a background task Task.Wait or Task.Result await
Wait for any task to complete Task.WaitAny await Task.WhenAny
Retrieve the results of multiple tasks Task.WaitAll await Task.WhenAll
Wait a period of time Thread.Sleep await Task.Delay
*/
// Sample demostration on asynchronous calls
// https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f
// https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
namespace Asynchronous
{
class MyButton
{
public bool Enabled { get; set; }
}
class AsyncMethods
{
// sample only text output
//private string url = "https://tibmanus.keybase.pub/hello.txt";
private string url = "https://os.mbed.com/media/uploads/alonsoflxestevez/hello2.txt.txt";
MyButton button1 = new MyButton();
public async Task DelayAsync() // NEW asynchronous method
{
// Code here runs in the original context.
await Task.Delay(100); // Do asynchronous work.
// Code here runs in the original context.
await Task.FromResult(1);
await Task.Delay(0).ConfigureAwait(continueOnCapturedContext: false);
// Code here runs in the original context.
await Task.Delay(100).ConfigureAwait(continueOnCapturedContext: false);
// Code here runs without the original context (in this case, on the thread pool).
}
// OK to return void ONLY if [asynchronous event handlers]
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
try
{
await DelayAsync(); // Can't use ConfigureAwait here. Inside, YES!
}
finally
{
button1.Enabled = true; // Because we need the context here.
}
}
// AVOID Task.Run at ALL cost in ASP.NET applications
private async void button2_Click(object sender, EventArgs e)
{
// Conclusion: do not use Task.Run in the implementation of the method; instead,
// use Task.Run to call the method.
await Task.Run(() => DoExpensiveCalculation(null));
}
public async Task<string> DoCurlAsync()
{
// Execution is synchronous here
using (var client = new HttpClient())
{
// Execution of GetFirstCharactersCountAsync() is yielded to the caller here
// GetStringAsync returns a Task<string>, which is *awaited*
var page = await client.GetStringAsync(url);
// Execution resumes when the client.GetStringAsync task completes,
// becoming synchronous again.
return page;
}
}
// use Async Await for I/O bound job. a example code
public async Task<string> DoCurlAsyncConfigureAwait()
{
using (var httpClient = new HttpClient())
using (var httpResponse = await httpClient.GetAsync(url).ConfigureAwait(false))
{
return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
// use Task.Run for CPU bound job. a example
public async Task<int> CalculateResult(object data)
{
// This queues up the work on the threadpool.
var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));
// Note that at this point, you can do some other work concurrently,
// as CalculateResult() is still executing!
// Execution of CalculateResult is yielded here!
var result = await expensiveResultTask;
return result;
}
private int DoExpensiveCalculation(object data) { return 1; }
public void DoTaskList()
{
var list = new ConcurrentBag<string>();
string[] dirNames = { ".", ".." };
List<Task> tasks = new List<Task>();
foreach (var dirName in dirNames)
{
Task t = Task.Run(() => {
foreach (var path in Directory.GetFiles(dirName))
list.Add(path);
});
tasks.Add(t);
}
// BAD way, use [await Task.WhenAll], and make method [async]
Task.WaitAll(tasks.ToArray());
foreach (Task t in tasks)
{
Console.WriteLine("Task {0} Status: {1}", t.Id, t.Status);
}
Console.WriteLine("Number of files read: {0}", list.Count);
}
}
class Program
{
private static AsyncMethods am = new AsyncMethods();
// lamba function pointer declaration
public delegate int LambdaFunc(int x, int y);
static public void Delay() // OLD synchronous method
{
Thread.Sleep(100); // Do synchronous work.
}
static async Task<string> AsyncWrapper()
{
return await am.DoCurlAsync(); // can only be used in async method
}
static async Task<string> AsyncWrapperConfigureAwait()
{
return await am.DoCurlAsyncConfigureAwait().ConfigureAwait(false); // prefered!!
}
static void Main(string[] args)
{
// unconventional, use if no return value // DEADLOCK possible in GUI or ASP.NET
am.DoCurlAsync().Wait(); // BAD: ignore return
am.DoCurlAsync().Wait(200); // BAD: imeout the async funtion within time
// get result immediately
var v1 = am.DoCurlAsync().Result; // NOT good in UI, such as button click
Console.WriteLine($"v1: {v1}"); // WILL DEADLOCK
var v2 = am.DoCurlAsyncConfigureAwait().Result; // OK, code within configuresawait
Console.WriteLine($"v2: {v2}");
var v3 = Program.AsyncWrapper().Result; // NOT good in UI, Deadlock
Console.WriteLine($"v3: {v3}");
var v4 = Program.AsyncWrapperConfigureAwait().Result; // PREFERRED!
Console.WriteLine($"v4: {v4}");
// Task related
var v5 = Task.Run(() => { return "Task.Run"; });
Console.WriteLine($"v5: {v5.Result}");
var v6 = Program.AsyncWrapper();
v6.Wait(); // BAD IDEA!
Console.WriteLine($"v6: {v6.Result}");
var v7 = Program.AsyncWrapperConfigureAwait();
v7.Wait(); // better
Console.WriteLine($"v7: {v7.Result}");
// Task LIST
am.DoTaskList(); // BAD way, use [await Task.WhenAll]
// WAYS to start as Task
// most direct way
Task.Factory.StartNew(() => { Console.WriteLine("Hello Task library!"); });
// using action!
Task t1 = new Task(new Action(Program.Delay));
t1.Start();
// using delegate
Task t2 = new Task(delegate { Program.Delay(); });
t2.Start();
// lambda
Task task = new Task(() => Program.Delay());
task.Start();
// Using Task.Run in .NET4.5
Task.Run(() => Program.Delay()); // need await/async
// Using Task.FromResult from lambda
LambdaFunc GetSum = (x, y) => { return x + y; };
LambdaFunc GetMin = (x, y) => { return x - y; };
LambdaFunc GetMut = (x, y) => { return x * y; };
LambdaFunc GetDiv = (x, y) => { return x * y; };
var v8 = Task.FromResult<int>(GetSum(2, 6)); // need await/async
v8.Wait();
Console.WriteLine($"v8: {v8.Result}");
}
}
}