C#TasksAsyncAwait

coding

最近一段时间在Youtube上面看了不少关于计算机方面的编程视频,其中UP主AngelSix的C#视频个人感觉讲得还可以,C# Tasks Async Await这个视频讲解了C#中的任务以及结合Async/Await的用法。Github仓库对应的代码地址为:TasksInConsole
不过个人感觉微软的官方文档讲解得更加详细,官方网址为:使用Async和Await的任务异步编程(TAP)模型 C#
这节视频的相关代码如下:

using System;

using System.Net;

using System.Threading;

using System.Threading.Tasks;

namespace TasksInConsole

{

class Program

{

#region Private Members

/// <summary>

/// The event finished callback for the Thread event example

/// </summary>

private static event Action EventFinished = () => { };

/// <summary>

/// Whether to run the thread examples

/// </summary>

private static bool RunThreadExamples = false;

#endregion

static void Main(string[] args)

{

// Log it

Log("Hello World!");

//

// Author: Luke Malpass

// License: MIT

// Support Me: https://www.patreon.com/angelsix

// Source Code: http://www.github.com/angelsix/youtube/Tasks

// Website: http://www.angelsix.com

// Contact: contact@angelsix.com

//

//

// What is Asynchronous

// ======================

//

// Asynchronous is if you start something, and don't wait while its happening.

// It literally means to not occur at the same time.

//

// This means not that our code returns early, but rather it doesn't sit there

// blocking the code while it waits (doesn't block the thread)

//

//

// Issues with Threads

// =====================

//

// Threads are asynchronous, as they naturally do something while the calling thread

// that made it doesn't wait for it.

//

#region Threads are asynchronous

if (RunThreadExamples)

{

// Log it

Log("Before first thread");

// Start new thread

new Thread(() =>

{

// Sleep a little

Thread.Sleep(500);

// Log it

Log("Inside first thread");

}).Start();

// Log it

Log("After first thread");

// Wait for work to finish

Thread.Sleep(1000);

Console.WriteLine("---------------------------------------------");

}

#endregion

// What's the issue with Threads?

//

// 1. Expensive to make

// 2. Not natural to be able to resume after a thread has finished to do something

// related to the thread that created it

//

// Issue 1 was solved with a ThreadPool. However issue 2 is still an issue for threads,

// and is one reason why Tasks were made.In order to resume work after some

// asynchronous operation has occurred we could with a Thread:

//

// 1. Block your code waiting for it (no better than just doing it on same thread)

//

#region Blocking Wait

if (RunThreadExamples)

{

// Log it

Log("Before blocking thread");

// Create new thread

var blockingThread = new Thread(() =>

{

// Sleep a little

Thread.Sleep(500);

// Log it

Log("Inside blocking thread");

});

// Start thread

blockingThread.Start();

// Block and wait

blockingThread.Join();

// Log

Log("After blocking thread");

Console.WriteLine("---------------------------------------------");

}

#endregion

// 2. Constantly poll for completion, waiting for a bool flag to say done (inefficient, slow)

#region Polling Wait

if (RunThreadExamples)

{

// Log it

Log("Before polling thread");

// Create poll flag

var pollComplete = false;

// Create thread

var pollingThread = new Thread(() =>

{

// Log it

Log("Inside polling thread");

// Sleep a little

Thread.Sleep(500);

// Set flag complete

pollComplete = true;

});

// Start thread

pollingThread.Start();

// Poll for completion

while (!pollComplete)

{

// Log it

Log("Polling....");

// Sleep a little

Thread.Sleep(100);

}

// Log it

Log("After polling thread");

Console.WriteLine("---------------------------------------------");

}

#endregion

// 3. Event-based callbacks (lose the calling thread on callback, and causes nesting)

#region Event-based Wait

if (RunThreadExamples)

{

// Log it

Log("Before event thread");

// Create thread

var eventThread = new Thread(() =>

{

// Log it

Log("Inside event thread");

// Sleep a little

Thread.Sleep(500);

// Fire completed event

EventFinished();

});

// Hook into callback event

EventFinished += () =>

{

// Log it

Log("Event thread callback on complete");

};

// Start thread

eventThread.Start();

// Log it

Log("After event thread");

// Wait for work to finish

Thread.Sleep(1000);

Console.WriteLine("---------------------------------------------");

}

#endregion

#region Event-based Wait Method

if (RunThreadExamples)

{

// Log it

Log("Before event method thread");

// Call event callback style method

EventThreadCallbackMethod(() =>

{

// Log it

Log("Event thread callback on complete");

});

// Log it

Log("After event method thread");

// Wait for work to finish

Thread.Sleep(1000);

Console.WriteLine("---------------------------------------------");

}

#endregion

//

// However that makes every time we want to do something asynchronous a lot of code

// and not easy to follow.

//

//

// What is a Task

// ================

//

// A Task encapsulates the promise of an operation completing in the future

//

//

// Tasks, Async and Await

// ========================

//

// Async in C# is mainly 2 words. async and await

//

// The point is to allow easy and clean asynchronous code to be written without complex or messy code.

//

#region Sync vs Async Method

// Log it

Log("Before sync thread");

// Website to fetch

var website = "http://www.google.co.uk";

// Download the string

WebDownloadString(website);

// Log it

Log("After sync thread");

Console.WriteLine("---------------------------------------------");

// Log it

Log("Before async thread");

// Download the string asynchronously

var downloadTask = WebDownloadStringAsync(website);

// Log it

Log("After async thread");

// Wait for task to complete

downloadTask.Wait();

Console.WriteLine("---------------------------------------------");

var task = Task.Run(async () =>

{

// Log it

Log("Before async await thread");

// Download the string asynchronously

await WebDownloadStringAsync(website);

// Log it

Log("After async await thread");

Console.WriteLine("---------------------------------------------");

});

// Wait the main task

task.Wait();

#endregion

// Async and await are always used together. A method or lambda tagged with

// async can then await any Task

//

// When you await something, the thread which called the await is free to then return to

// what it was doing, while in parallel the task inside the await is now run on another thread.

//

// Once the task is done, it returns either to the original calling thread, or carries on,

// on another thread to do the work that codes after the await.

//

//

//

// Async Analogy

// ===============

//

// Imagine you go to Starbucks and the entire shop is run by one person.

// His name is Mr UI Thread. You walk in and ask Mr Thread for a Vanilla Latte.

// He obliges and starts to make your coffee.

//

// He puts the milk into the container and turns on the hot steam, and proceeds

// to stand there and wait for the milk to reach 70 degrees.

//

// During this time you remember you wanted a muffin as well, so you shout over

// to Mr Thread and ask for a muffin... but he ignores you. He is blocked

// waiting for the milk to boil.

//

// Several minutes goes by and 3 more customers have come in and are waiting

// to be served. Finally the milk is finished and he completes the Latte.

// Returning to you. You are a little annoyed at being ignored for minutes

// and decide to leave your muffin.

//

// Then he continues to serve one customer at a time, doing one job at a time.

// Not a good situation.

//

// This is what happens with a single threaded application.

//

// Now in order to improve business Mr Thread employs 2 new members of staff

// called Mrs and Mrs Worker Thread. The pair work well independently and

// as Mr Thread takes orders from the customers, he asks Mrs Worker Thread

// to complete the order, and then without waiting for Mrs Worker Thread to

// finish the drink, proceeds to serve the next customer.

//

// Once Mrs Worker Thread has finished a drink, instead of having to take

// the drinks to the customers she asks Mr Worker Thread to serve the drinks

// and then without waiting she proceeds to start the next order.

//

// The business is now a well-oiled, multi-threaded business.

//

//

// The Synchronous part in Tasks

// ===============================

//

//

#region The Synchronous Part of Tasks

// Run some work to show the synchronous parts of the call

Task.Run(async () =>

{

// Log it

Log($"Before DoWork thread");

// Do work

// This will return a Task and run the lines of code inside the method

// up until the point at which the first await is hit

var doWorkTask = DoWorkAsync("me");

// Await the task

// This will then spin off to a new thread and come with the result

await doWorkTask;

// Log it

Log($"After DoWork thread");

}).Wait();

Console.WriteLine("---------------------------------------------");

#endregion

//

// Async Return Types

// ====================

//

// You can only return void, Task or Task<T> for a method marked as async, as the method is

// not complete when it returns, so no other result is valid.

//

#region Method 1 Getting Result of Async From Sync

// Get the task

var doWorkResultTask = DoWorkAndGetResultAsync("Return this");

// Wait for it

doWorkResultTask.Wait();

// Get the result

var doWorkResult = doWorkResultTask.Result;

Console.WriteLine("---------------------------------------------");

#endregion

#region Method 2 Getting Result of Async From Sync

Task.Run(async () =>

{

var doWorkResult2 = await DoWorkAndGetResultAsync("Return this 2");

}).Wait();

Console.WriteLine("---------------------------------------------");

#endregion

//

// Async Keyword

// ===============

//

// The async keyword is not actually added to the method declaration signature,

// the only effect is has is to change the compiled code.

//

// That's why interfaces cannot declare async, as it isn't a declarative statement,

// its a compilation statement to change the flow of the function.

//

//

// Consuming Async Methods

// =========================

//

// The best way to consume or call a method that returns a task is to be async yourself

// in the caller method, to ultimately awaiting it.

//

// By that definition async methods are naturally contagious.

//

#region Consuming Wait

// Store the taks

var workResultTask = DoWorkAndGetResultAsync("Consume Wait");

// Wait for it

workResultTask.Wait();

// Get the result

var workResult = workResultTask.Result;

Console.WriteLine("---------------------------------------------");

#endregion

#region Consuming via Task

// Declare the result

var workResultViaTask = default(string);

// Store the taks

Task.Run(async () =>

{

// Get result

workResultViaTask = await DoWorkAndGetResultAsync("Consume via Task");

}).Wait();

Console.WriteLine("---------------------------------------------");

#endregion

//

// What happens during an Async call

// ===================================

//

// Code inside a function that returns a Task runs its code on the callers thread

// up until the first line that calls await. At this point in time:

//

// 1. The current thread executing your code is released (making your code asynchronous).

// This means from a normal point of view, your function has returned at this point

// (it has return the Task object).

//

// 2. Once the task you have awaited completes, your method should appear to continue

// from where it left off, as if it never returned from the method, so resume on

// the line below the await.

//

// To achieve this, C# at the point of reaching the await call:

//

// 1. Stores all local variables in the scope of the method

// 2. Stores the parameters of your method

// 3. The "this" variable to store all class-level variables

// 4. Stores all contexts(Execution, Security, Call)

//

// And on resuming to the line after the await, restores all of these values as if nothing

// had changed. All of this information is stored on the garbage collection heap.

//

//

// What is happening with threads during an async call

// =====================================================

//

// As you call a method that returns a `Task` and uses `async`, inside the method all code,

// up until the first `await` statement, is run like a normal function on the thread that

// called it.

//

// Once it hits the `await` the function returns the `Task` to the caller, and does its work

// thats inside the `await` call on a new thread (or existing).

//

// Once its done, and effectively "after" the `await` line, execution returns to a certain

// thread.

//

// That thread is determined by first checking if the thread has an synchronization context

// and if it does it asks that what thread to return to. For UI threads this will return work

// to the UI thread itself.

//

// Console application has no synchronization context

var syncContext = SynchronizationContext.Current;

//

// For normal threads that have no synchronization context, the code after the `await`

// typically, but not always, continues on the same thread that the inner work was being done

// on, but has no requirement to resume on any specific thread.

//

// Typically if you use `ContinueWith` instead of `await`, the code inside `ContinueWith` runs

// on a different thread than the inner task was running on, and using `await` typically

// continues on the same thread.

//

// Show ContinueWith typically changing thread ID's

DoWorkAsync("ContinueWith").ContinueWith(t =>

{

Log("ContinueWith Complete");

}).Wait();

Console.WriteLine("---------------------------------------------");

//

// This also means after every await the next line is typically on a new thread if there is no

// synchronization context.

//

// **********************************************************************************************

//

// An exception is if you use `ConfigureAwait(false)` then the SynchronizationContext is

// totally ignored and the resuming thread is treated as if there were no context.

//

// Resuming on the original thread via the synchronization context is an expensive thing

// (takes time) and so if you choose to not care about resuming on the same thread and want

// to save time you can use `ConfigureAwait` to remove that overhead

//

// **********************************************************************************************

//

//

// Exceptions in Async calls

// ===========================

//

// Any exceptions thrown that are not caught by the method itself are thrown into the Task

// objects value `IsFaulted` and the `Exception` property.

//

// If you do not await the Task, the exception will not throw on your calling thread.

//

#region Throw on Calling Thread, Without Awaiting

Log("Before ThrowAwait");

var crashedTask = ThrowAwait(true);

// Did it crash?

var isFaulted = crashedTask.IsFaulted;

// The exception

Log(crashedTask.Exception.Message);

Log("After ThrowAwait");

#endregion

//

// If you await, the exception will rethrow onto the caller thread that awaited it.

//

// The exception to the rule is a method with async void. As it cannot be awaited, any

// exceptions that occur in an async void method are re-thrown like this:

//

// 1. If there is a synchronization context the exception is Post back to the caller thread.

// 2. If not, it is thrown on the thread pool

//

#region Throw on Calling Thread, Without Awaiting

Log("Before ThrowVoid");

ThrowAwaitVoid(true);

Log("After ThrowVoid");

#endregion

Console.ReadLine();

}

#region Helper Methods

/// <summary>

/// Output a message with the current thread ID appended

/// </summary>

/// <param name="message"></param>

private static void Log(string message)

{

// Write line

Console.WriteLine($"{message} [{Thread.CurrentThread.ManagedThreadId}]");

}

#endregion

#region Thread Methods

/// <summary>

/// Shows an event-based thread callback via a method

/// </summary>

/// <param name="completed">The callback to call once the work is complete</param>

private static void EventThreadCallbackMethod(Action completed)

{

// Start a new thread

new Thread(() =>

{

// Log it

Log("Inside event thread method");

// Sleep

Thread.Sleep(500);

// Fire completed event

completed();

}).Start();

}

#endregion

#region Task Example Methods

/// <summary>

/// Downloads a string from a website URL sychronously

/// </summary>

/// <param name="url">The URL to download</param>

private static void WebDownloadString(string url)

{

// Synchronous pattern

var webClient = new WebClient();

var result = webClient.DownloadString(url);

// Log

Log($"Downloaded {url}. {result.Substring(0, 10)}");

}

/// <summary>

/// Downloads a string from a website URL asychronously

/// </summary>

/// <param name="url">The URL to download</param>

private static async Task WebDownloadStringAsync(string url)

{

// Asynchronous pattern

var webClient = new WebClient();

var result = await webClient.DownloadStringTaskAsync(new Uri(url));

// Log

Log($"Downloaded {url}. {result.Substring(0, 10)}");

}

/// <summary>

/// Does some work asynchronously for somebody

/// </summary>

/// <param name="forWho">Who we are doing the work for</param>

/// <returns></returns>

private static async Task DoWorkAsync(string forWho)

{

// Log it

Log($"Doing work for {forWho}");

// Start a new task (so it runs on a different thread)

await Task.Run(async () =>

{

// Log it

Log($"Doing work on inner thread for {forWho}");

// Wait

await Task.Delay(500);

// Log it

Log($"Done work on inner thread for {forWho}");

});

// Log it

Log($"Done work for {forWho}");

}

/// <summary>

/// Does some work asynchronously for somebody, and return a result

/// </summary>

/// <param name="forWho">Who we are doing the work for</param>

/// <returns></returns>

private static async Task<string> DoWorkAndGetResultAsync(string forWho)

{

// Log it

Log($"Doing work for {forWho}");

// Start a new task (so it runs on a different thread)

await Task.Run(async () =>

{

// Log it

Log($"Doing work on inner thread for {forWho}");

// Wait

await Task.Delay(500);

// Log it

Log($"Done work on inner thread for {forWho}");

});

// Log it

Log($"Done work for {forWho}");

// Return what we received

return forWho;

}

/// <summary>

/// Throws an exception inside a task

/// </summary>

/// <param name="before">Throws the exception before an await</param>

/// <returns></returns>

private static async Task ThrowAwait(bool before)

{

if (before)

throw new ArgumentException("Oopps");

await Task.Delay(1);

throw new ArgumentException("Oopps");

}

/// <summary>

/// Throws an exception inside a void async before awaiting

/// </summary>

/// <param name="before">Throws the exception before an await</param>

/// <returns></returns>

private static async void ThrowAwaitVoid(bool before)

{

if (before)

throw new ArgumentException("Oopps");

await Task.Delay(1);

throw new ArgumentException("Oopps");

}

#endregion

}

}

整个视频的源代码可以从他的Github仓库下载:

git clone https://github.com/angelsix/youtube.git


本文同步分享在 博客“雪域迷影”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

以上是 C#TasksAsyncAwait 的全部内容, 来源链接: utcz.com/z/510038.html

回到顶部