Concurrency in C# Cookbook 笔记

Pausing for a Period of Time
Problem:
You need to (asynchronously) wait for a period of time. This can be useful when unit
testing or implementing retry delays. This solution can also be useful for simple time‐
outs.
Solution:
The Task type has a static method Delay that returns a task that completes after the
specified time。

This example defines a task that completes asynchronously, for use with unit testing.
When faking an asynchronous operation, it’s important to test at least synchronous
success and asynchronous success as well as asynchronous failure. This example returns
a task used for the asynchronous success case:

static async Task<T> DelayResult<T>(T result, TimeSpan delay)
{
await Task. Delay(delay);
return result;
}

This next example is a simple implementation of an exponential backoff, that is, a retry
strategy where you increase the delays between retries. Exponential backoff is a best
practice when working with web services to ensure the server does not get flooded with
retries.
For production code, I would recommend a more thorough solu‐
tion, such as the Transient Error Handling Block in Microsoft’s En‐
terprise Library; the following code is just a simple example of
Task.Delay usage.

static async Task<string> DownloadStringWithRetries(string uri)
{
 using (var client = new HttpClient())
 {
 // Retry after 1 second, then after 2 seconds, then 4.
var nextDelay = TimeSpan. FromSeconds(1);
 for (int i = 0; i != 3; ++i)
 {
 try
 {
 return await client. GetStringAsync(uri);
 }
 catch
 {
 }
 await Task. Delay(nextDelay);
 nextDelay = nextDelay + nextDelay;
 }
 // Try one last time, allowing the error to propogate.
 return await client. GetStringAsync(uri);
 }
}

This final example uses Task.Delay as a simple timeout; in this case, the desired se‐
mantics are to return null if the service does not respond within three seconds:

static async Task<string> DownloadStringWithTimeout(string uri)
{
using (var client = new HttpClient())
{
var downloadTask = client. GetStringAsync(uri);
var timeoutTask = Task. Delay(3000);
var completedTask = await Task. WhenAny(downloadTask, timeoutTask);
if (completedTask == timeoutTask)
return null;
return await downloadTask;
}
}

Discussion
Task.Delay is a fine option for unit testing asynchronous code or for implementing
retry logic. However, if you need to implement a timeout, a CancellationToken is usu‐
ally a better choice.
See Also
Recipe 2.5 covers how Task.WhenAny is used to determine which task completes first.
Recipe 9.3 covers using CancellationToken as a timeout

2.2. Returning Completed Tasks
Problem
You need to implement a synchronous method with an asynchronous signature. This
situation can arise if you are inheriting from an asynchronous interface or base class
but wish to implement it synchronously. This technique is particularly useful when unit
testing asynchronous code, when you need a simple stub or mock for an asynchronous
interface.
Solution
You can use Task.FromResult to create and return a new Task<T> that is already com‐
pleted with the specified value

interface IMyAsyncInterface
{
 Task<int> GetValueAsync();
}
class MySynchronousImplementation : IMyAsyncInterface
{
 public Task<int> GetValueAsync()
 {
 return Task. FromResult(13);
 }
}

2.3. Reporting Progress
Problem
You need to respond to progress while an asynchronous operation is executing.
Solution
Use the provided IProgress<T> and Progress<T> types. Your async method should
take an IProgress<T> argument; the T is whatever type of progress you need to report

static async Task MyMethodAsync(IProgress<double> progress = null)
{
 double percentComplete = 0;
 while (! done)
 {
 ...
 if (progress != null)
 progress. Report(percentComplete);
 }
}

Calling code can use it as such:

static async Task CallMyMethodAsync()
{
 var progress = new Progress<double>();
 progress. ProgressChanged += (sender, args) =>
 {
 ...
 };
 await MyMethodAsync(progress);
}

By convention, the IProgress<T> parameter may be null if the caller does not need
progress reports, so be sure to check for this in your async method.
Bear in mind that the IProgress<T>.Report method may be asynchronous. This means
that MyMethodAsync may continue executing before the progress is actually reported.
For this reason, it’s best to define T as an immutable type or at least a value type. If T is
a mutable reference type, then you’ll have to create a separate copy yourself each time
you call IProgress<T>.Report.
Progress<T> will capture the current context when it is constructed and will invoke its
callback within that context. This means that if you construct the Progress<T> on the
UI thread, then you can update the UI from its callback, even if the asynchronous
method is invoking Report from a background thread.
When a method supports progress reporting, it should also make a best effort to support
cancellation

If all the tasks have the same result type and they all complete successfully, then the
Task.WhenAll task will return an array containing all the task results:

Task task1 = Task. FromResult(3);
Task task2 = Task. FromResult(5);
Task task3 = Task. FromResult(7);
int[] results = await Task. WhenAll(task1, task2, task3);
// "results" contains { 3, 5, 7 }

There is an overload of Task.WhenAll that takes an IEnumerable of tasks; however, I
do not recommend that you use it. Whenever I mix asynchronous code with LINQ, I
find the code is clearer when I explicitly “reify” the sequence (i.e., evaluate the sequence,
creating a collection):

static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
 var httpClient = new HttpClient();
 // Define what we‘re going to do for each URL.
 var downloads = urls. Select(url => httpClient. GetStringAsync(url));
 // Note that no tasks have actually started yet
 // because the sequence is not evaluated.
 // Start all URLs downloading simultaneously.
 Task<string>[] downloadTasks = downloads. ToArray();
 // Now the tasks have all started.
 // Asynchronously wait for all downloads to complete.
 string[] htmlPages = await Task. WhenAll(downloadTasks);
 return string. Concat(htmlPages);
}
// Returns the length of data at the first URL to respond.
private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)
{
 var httpClient = new HttpClient();
 // Start both downloads concurrently.
 Task<byte[]> downloadTaskA = httpClient. GetByteArrayAsync(urlA);
 Task<byte[]> downloadTaskB = httpClient. GetByteArrayAsync(urlB);
 // Wait for either of the tasks to complete.
 Task<byte[]> completedTask =
 await Task. WhenAny(downloadTaskA, downloadTaskB);
 // Return the length of the data retrieved from that URL.
 byte[] data = await completedTask;
 return data. Length;
}
static async Task<int> DelayAndReturnAsync(int val)
{
 await Task. Delay(TimeSpan. FromSeconds(val));
 return val;
}
static async Task AwaitAndProcessAsync(Task<int> task)
{
 var result = await task;
 Trace. WriteLine(result);
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
 // Create a sequence of tasks.
 Task<int> taskA = DelayAndReturnAsync(2);
 Task<int> taskB = DelayAndReturnAsync(3);
 Task<int> taskC = DelayAndReturnAsync(1);
 var tasks = new[] { taskA, taskB, taskC };
 var processingTasks = (from t in tasks
 select AwaitAndProcessAsync(t)). ToArray();
 // Await all processing to complete
 await Task. WhenAll(processingTasks);
}

Alternatively, this can be written as:

static async Task<int> DelayAndReturnAsync(int val)
{
 await Task. Delay(TimeSpan. FromSeconds(val));
 return val;
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
 // Create a sequence of tasks.
 Task<int> taskA = DelayAndReturnAsync(2);
 Task<int> taskB = DelayAndReturnAsync(3);
 Task<int> taskC = DelayAndReturnAsync(1);
 var tasks = new[] { taskA, taskB, taskC };
 var processingTasks = tasks. Select(async t =>
 {
 var result = await t;
 Trace. WriteLine(result);
 }). ToArray();
 // Await all processing to complete
 await Task. WhenAll(processingTasks);
}
时间: 2024-08-10 16:10:51

Concurrency in C# Cookbook 笔记的相关文章

iOS7 Programming Cookbook笔记

Chapter 1 Adding Object and Index Subscription Support 类似Dictionary和Array那种用下标来访问. @interface Person : NSObject @property (nonatomic, copy) NSString *firstName; @property (nonatomic, copy) NSString *lastName; // object subscription - (id) objectForKe

[it-ebooks]电子书列表

#### it-ebooks电子书质量不错,但搜索功能不是很好 #### 格式说明  [ ]中为年份      ||  前后是标题和副标题  #### [2014]: Learning Objective-C by Developing iPhone Games || Leverage Xcode and Objective-C to develop iPhone games http://it-ebooks.info/book/3544/ Learning Web App Developmen

(转) [it-ebooks]电子书列表

[it-ebooks]电子书列表 [2014]: Learning Objective-C by Developing iPhone Games || Leverage Xcode and Objective-C to develop iPhone games http://it-ebooks.info/book/3544/Learning Web App Development || Build Quickly with Proven JavaScript Techniques http://

Redis 之(二) Redis的基本数据结构以及一些常用的操作

本篇内容是Redis最简单最容易掌握的知识,如果你已经熟知了,就可以选择跳过啦! 要体验Redis,那么首先你得安装Redis,这边的话我就只讲一下Windows环境下的安装与操作: Window 下安装 下载地址:https://github.com/MSOpenTech/redis/releases. Redis 支持 32 位和 64 位.这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C 盘,解压后,将文件夹重新命名为 redis. 打开一

仓储模式Repository的选择与设计

1.项目小,扩展性差 public interface IRepository<T> where T : class,new() { /// <summary> /// 创建对象 /// </summary> /// <param name="model"></param> /// <returns></returns> T Create(T model); /// <summary> //

Redis 基础数据结构

string (字符串) 是一种键值对的数据结构,定义一个唯一的key值来获取相应的value数据. > set name codehole OK > get name "codehole" > exists name (integer) 1 > del name (integer) 1 > get name (nil) 批量操作 > set name1 codehole OK > set name2 holycoder OK > mge

Java 7 Concurrency Cookbook 翻译 序言

在日常的Java代码开发过程中,很难免地有对多线程的需求,掌握java多线程和并发的机制也是Java程序员写出更健壮和高效代码的基础.笔者找寻国内已出版的关于Java多线程和并发的的中文书籍和翻译书籍,大家一致推荐的是<Java Concurrency in Practice>,笔者暂时还没有看英文原版,笔者看的是它的翻译版<Java并发编程实战>,笔者读起来感觉并不通畅,不知道是翻译的问题还是原版本来就写得不够流畅,同时感觉知识的深度也超过了入门的需求. 笔者在机缘巧合之下,发现

NHibernate.3.0.Cookbook第一章第六节Handling versioning and concurrency的翻译

NHibernate.3.0.Cookbook第一章第六节Handling versioning and concurrency的翻译 第一章第二节Mapping a class with XML第一章第三节Creating class hierarchy mappings第一章第四节Mapping a one-to-many relationship第一章第五节Setting up a base entity class Handling versioning and concurrency

MySQL Cookbook读书笔记第三章

1,查询指定列/从指定列中查询 若需要指定每一列在查询结果中出现的先后顺序,可以在select语句中指定(dstuser在dsthost之后): 查询显示用户所关注的列: 2,指定查询行 使用where关键字可以查询符合条件限制的数据例如:查找srchost为venus或者srchost以s开头的行记录: 使用操作符like进行模式匹配,其中%表示通配符,代表一个差多为任意值的字符串. where使用操作符and可以查询多个条件: 条件并集: 3,格式化显示查询结果 当查询语句中没有重命名列名,