# Concurrency in C# Cookbook中文翻译 ：1.1.并发性概述：异步编程简介

Chapter 1. Concurrency: An Overview 第1章。并发性:概述

Concurrency is a key aspect of beautiful software. For decades, concurrency was possible but difficult to achieve. Concurrent software was difficult to write,difficult to debug, and difficult to maintain. As a result, many developers chose the easier path and avoided concurrency. With the libraries and language features available for modern .NET programs, concurrency is now much easier.

Microsoft has led the way in significantly lowering the bar for concurrency.Previously, concurrent programming was the domain of experts; these days,every developer can (and should) embrace concurrency.

# 1 Introduction to Concurrency 介绍并发

Before continuing, I’d like to clear up some terminology that I’ll be using throughout this book. These are my own definitions that I use consistently to disambiguate different programming techniques. Let’s start with concurrency.

## 1.1 Concurrency：Doing more than one thing at a time.

I hope it’s obvious how concurrency is helpful. End-user applications use concurrency to respond to user input while writing to a database. Server applications use concurrency to respond to a second request while finishing the first request. You need concurrency any time you need an application to do one thing while it’s working on something else. Almost every software application in the world can benefit from concurrency.Most developers hearing the term “concurrency” immediately think of “multithreading.” I’d like to draw a distinction between these two.

## 1.2 Multithreading：A form of concurrency that uses multiple threads of execution.

Multithreading refers to literally using multiple threads. As demonstrated in many recipes in this book, multithreading is one form of concurrency, but certainly not the only one. In fact, direct use of low-level threading types has almost no purpose in a modern application; higher-level abstractions are more powerful and more efficient than old-school multithreading. For that reason, I’ll minimize my coverage of outdated techniques. None of the multithreading recipes in this book use the Thread or BackgroundWorker types; they have been replaced with superior alternatives.

WARNING警告

As soon as you type new Thread(), it’s over; your project already has legacy code.

But don’t get the idea that multithreading is dead! Multithreading lives on in the thread pool, a useful place to queue work that automatically adjusts itself according to demand. In turn, the thread pool enables another important form of concurrency: parallel processing.

## 1.3 Parallel processing：Doing lots of work by dividing it up among multiple threads that run concurrently.

Parallel processing (or parallel programming) uses multithreading to maximize the use of multiple processor cores. Modern CPUs have multiple cores, and if there’s a lot of work to do, then it makes no sense to make one core do all the work while the others sit idle. Parallel processing splits the work among multiple threads, which can each run independently on a different core.

Parallel processing is one type of multithreading, and multithreading is one type of concurrency. There’s another type of concurrency that is important in modern applications but isn’t as familiar to many developers: asynchronous programming.

## 1.4 Asynchronous programming：A form of concurrency that uses futures or callbacks to avoid unnecessary threads.

A future (or promise) is a type that represents some operation that will complete in the future. Some modern future types in .NET are Task and Task. Older asynchronous APIs use callbacks or events instead of futures. Asynchronous programming is centered around the idea of an asynchronous operation: some operation that is started that will complete some time later. While the operation is in progress, it doesn’t block the original thread; the thread that starts the operation is free to do other work. When the operation completes, it notifies its future or invokes its callback or event to let the application know the operation is finished.

Asynchronous programming is a powerful form of concurrency, but until recently, it required extremely complex code. The async and await support in modern languages make asynchronous programming almost as easy as synchronous (nonconcurrent) programming.

Another form of concurrency is reactive programming. Asynchronous programming implies that the application will start an operation that will complete once at a later time. Reactive programming is closely related to asynchronous programming but is built on asynchronous events instead of asynchronous operations. Asynchronous events may not have an actual “start,” may happen at any time, and may be raised multiple times. One example is user input.

## 1.5 Reactive programming：A declarative style of programming where the application reacts to events.

If you consider an application to be a massive state machine, the application’s behavior can be described as reacting to a series of events by updating its state at each event. This isn’t as abstract or theoretical as it sounds; modern frameworks make this approach quite useful in real-world applications. Reactive programming isn’t necessarily concurrent, but it is closely related to concurrency, so this book covers the basics.

Usually, a mixture of techniques is used when writing a concurrent program. Most applications at least use multithreading (via the thread pool) and asynchronous programming. Feel free to mix and match all the various forms of concurrency, using the appropriate tool for each part of the application.

# 2. Introduction to Asynchronous Programming 异步编程简介

Asynchronous programming has two primary benefits. The first benefit is for end-user GUI programs: asynchronous programming enables responsiveness. Everyone has used a program that temporarily locks up while it’s working; an asynchronous program can remain responsive to user input while it’s working.

The second benefit is for server-side programs: asynchronous programming enables scalability. A server application can scale somewhat just by using the thread pool, but an asynchronous server application can usually scale an order of magnitude better than that.

Both benefits of asynchronous programming derive from the same underlying aspect: asynchronous programming frees up a thread. For GUI programs,asynchronous programming frees up the UI thread; this permits the GUI application to remain responsive to user input. For server applications,asynchronous programming frees up request threads; this permits the server to use its threads to serve more requests.

Modern asynchronous .NET applications use two keywords: async and await. The async keyword is added to a method declaration, and performs a double purpose: it enables the await keyword within that method and it signals the compiler to generate a state machine for that method, similar to how yield return works. An async method may return Task if it returns a value, Task if it doesn’t return a value, or any other “task-like” type, such as ValueTask. In addition, an async method may return IAsyncEnumerable or IAsyncEnumerator if it returns multiple values in an enumeration. The task-like types represent futures; they can notify the calling code when the async method completes.

With that background, let’s take a quick look at an example:

async Task DoSomethingAsync()
{
int value = 13;
// Asynchronously wait 1 second.
value *= 2;
// Asynchronously wait 1 second.
Trace.WriteLine(value);
}


An async method begins executing synchronously, just like any other method.Within an async method, the await keyword performs an asynchronous wait on its argument. First, it checks whether the operation is already complete; if it is, it continues executing (synchronously). Otherwise, it will pause the async method and return an incomplete task. When that operation completes some time later, the async method will resume executing.

You can think of an async method as having several synchronous portions,broken up by await statements. The first synchronous portion executes on whatever thread calls the method, but where do the other synchronous portions execute? The answer is a bit complicated.

When you await a task (the most common scenario), a context is captured when the await decides to pause the method. This is the current SynchronizationContext unless it’s null, in which case the context is the current TaskScheduler. The method resumes executing within that captured context. Usually, this context is the UI context (if you’re on the UI thread) or the threadpool context (most other situations). If you have an ASP.NET Classic (pre-Core) application, then the context could also be an ASP.NET request context. ASP.NET Core uses the threadpool context rather than a special request context.

So, in the preceding code, all the synchronous portions will attempt to resume on the original context. If you call DoSomethingAsync from a UI thread, each of its synchronous portions will run on that UI thread; but if you call it from a threadpool thread, each of its synchronous portions will run on any threadpool thread.

You can avoid this default behavior by awaiting the result of the ConfigureAwait extension method and passing false for the continueOnCapturedContext parameter. The following code will start on the calling thread, and after it is paused by an await, it’ll resume on a threadpool thread:

{
int value = 13;
// Asynchronously wait 1 second.
value *= 2;
// Asynchronously wait 1 second.
Trace.WriteLine(value);
}

It’s good practice to always call ConfigureAwait in your core “library” methods, and only resume the context when you need it—in your outer “user interface” methods.

The await keyword is not limited to working with tasks; it can work with any kind of awaitable that follows a certain pattern. As an example, the Base Class Library includes the ValueTask type, which reduces memory allocations if the result is commonly synchronous; for example, if the result can be read from an in-memory cache. ValueTask is not directly convertible to Task, but it does follow the awaitable pattern, so you can directly await it. There are other examples, and you can build your own, but most of the time await will take a Task or Task.

There are two basic ways to create a Task instance. Some tasks represent actual code that a CPU has to execute; these computational tasks should be created by calling Task.Run (or TaskFactory.StartNew if you need them to run on a particular scheduler). Other tasks represent a notification; these kinds of eventbased tasks are created by TaskCompletionSource (or one of its shortcuts). Most I/O tasks use TaskCompletionSource.

Error handling is natural with async and await. In the code snippet that follows, PossibleExceptionAsync may throw a NotSupportedException, but TrySomethingAsync can catch the exception naturally. The caught exception has its stack trace properly preserved and isn’t artificially wrapped in a TargetInvocationException or AggregateException:

async Task TrySomethingAsync()
{
try
{
await PossibleExceptionAsync();
}
catch (NotSupportedException ex)
{
LogException(ex);
throw;
}
}



When an async method throws (or propagates) an exception, the exception is placed on its returned Task and the Task is completed. When that Task is awaited, the await operator will retrieve that exception and (re)throw it in a way such that its original stack trace is preserved. Thus, code such as the following example would work as expected if PossibleExceptionAsync was an async method:

async Task TrySomethingAsync()
{
// The exception will end up on the Task, not thrown directly.
try
{
// The Task's exception will be raised here, at the await.
}
catch (NotSupportedException ex)
{
LogException(ex);
throw;
}
}


There’s one other important guideline when it comes to async methods: once you start using async, it’s best to allow it to grow through your code. If you call an async method, you should (eventually) await the task it returns. Resist the temptation to call Task.Wait, Task.Result, or GetAwaiter().GetResult(); doing so could cause a deadlock. Consider the following method:

async Task WaitAsync()
{
// This await will capture the current context ...
// ... and will attempt to resume the method here in that   context.
}
{
// Start the delay.
// Synchronously block, waiting for the async method to     complete.
}



The code in this example will deadlock if called from a UI or ASP.NET Classic context because both of those contexts only allow one thread in at a time.Deadlock will call WaitAsync, which begins the delay. Deadlock then (synchronously) waits for that method to complete, blocking the context thread.

When the delay completes, await attempts to resume WaitAsync within the captured context, but it cannot because there’s already a thread blocked in the context, and the context only allows one thread at a time. Deadlock can be prevented two ways: you can use ConfigureAwait(false) within WaitAsync (which causes await to ignore its context), or you can await the call to WaitAsync (making Deadlock into an async method).

WARNING:If you use async, it’s best to use async all the way.

For a more complete introduction to async, the online documentation that Microsoft has provided for async is fantastic; I recommend reading at least the Asynchronous Programming overview and the Task-based Asynchronous Pattern (TAP) overview. If you want to go a bit deeper, there’s also the Async in Depth documentation.

ps:https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

Asynchronous streams take the groundwork of async and await and extend it to handle multiple values. Asynchronous streams are built around the concept of asynchronous enumerables, which are like regular enumerables, except that they enable asynchronous work to be done when retrieving the next item in the sequence. This is an extremely powerful concept that Chapter 3 covers in more detail. Asynchronous streams are especially useful whenever you have a sequence of data that arrives either one at a time or in chunks. For example, if your application processes the response of an API that uses paging with limit and offset parameters, then asynchronous streams are an ideal abstraction. As of the time of this writing, asynchronous streams are only available on the newest .NET platforms.

02-12 629
06-22 1万+
11-05 221
05-06 1万+
01-03 1万+
©️2020 CSDN 皮肤主题: 点我我会动 设计师:白松林