http://www.cnblogs.com/savorboard/p/csharp-concurrency-cookbook.html

异步基础

任务暂停,休眠

异步方式暂停或者休眠任务,可以使用 Task.Delay();

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

异步重试机制

一个简单的指数退避策略,重试的时间会逐次增加,在访问 Web 服务时,一般采用此种策略。


static async Task<string> DownloadString(string uri) {
using (var client = new HttpClient()) { var nextDealy = TimeSpan.FromSeconds(1);
for (int i = 0; i != 3; ++i) {
try {
return await client.GetStringAsync(uri);
}
catch {
} await Task.Delay(nextDealy);
nextDealy = nextDealy + nextDealy;
} //最后重试一次,抛出出错信息
return await client.GetStringAsync(uri);
}
}

报告进度

异步操作中,经常需要展示操作进度,可以使用 IProcess<T> 和 Process<T>


static async Task MyMethodAsync(IProgress<double> progress) {
double precentComplete = 0;
bool done = false;
while (!done) {
await Task.Delay(100);
if (progress != null) {
progress.Report(precentComplete);
}
precentComplete++;
if (precentComplete == 100) {
done = true;
}
}
} public static void Main(string[] args) { Console.WriteLine("starting..."); var progress = new Progress<double>();
progress.ProgressChanged += (sender, e) => {
Console.WriteLine(e);
};
MyMethodAsync(progress).Wait(); Console.WriteLine("finished");
}

等待一组任务

同时执行几个任务,等待他们全部完成

Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
Task task3 = Task.Delay(TimeSpan.FromSeconds(1)); Task.WhenAll(task1, task2, task3).Wait();

等待任意一个任务完成

执行若干任务,只需要对其中一个的完成进行响应。主要用于对一个操作进行多种独立的尝试,只要其中一个尝试完成,任务就算完成。

static async Task<int> FirstResponseUrlAsync(string urlA, string urlB) {
var httpClient = new HttpClient(); Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB); Task<byte[]> completedTask = await Task.WhenAny(downloadTaskA, downloadTaskB); byte[] data = await completedTask; return data.Length;
}

集合

不可变栈和队列

需要一个不会经常修改,可以被多个线程安全访问的栈和队列。他们的API和 Stack<T> 和 Queue<T> 非常相似。性能上,不可变栈(LIFO)和队列(FIFO)与标准的栈和队列具有相同的时间复杂度。但是在需要频繁修改的简单情况下,标准栈和队列速度更快。

在内部实现上,当对一个对象进行覆盖(重新赋值)的时候,不可变集合采用的是返回一个修改过的集合,原始集合引用是不变化的,也就是说如果另外一个变量引用了相同的对象,那么它(另外的变量)是不会变化的。

ImmutableStack

var stack = ImmutableStack<int>.Empty;
stack = stack.Push(11);
var biggerstack = stack.Push(12); foreach (var item in biggerstack) {
Console.WriteLine(item);
} // output: 12 11 int lastItem;
stack = stack.Pop(out lastItem);
Console.WriteLine(lastItem); //output: 11

实际上,两个栈内部共享了存储 11 的内存,这种实现方式效率很高,而且每个实例都是线程安全的。

ImmutableQueue

var queue = ImmutableQueue<int>.Empty;
queue = queue.Enqueue(11);
queue = queue.Enqueue(12); foreach (var item in queue) {
Console.WriteLine(item);
} // output: 11 12 int nextItem;
queue = queue.Dequeue(out nextItem);
Console.WriteLine(nextItem); //output: 11

不可变列表和集合

ImmutableList

时间复杂度

操作 List ImmutableList
Add O(1) O(log N)
Insert O(log N) O(log N)
RemoveAt O(log N) O(log N)
Item[index] O(1) O(log N)

有些时候需要这样一个数据结构:支持索引,不经常修改,可以被多线程安全的访问。

var list = ImmutableList<int>.Empty;

list = list.Insert(0, 11);
list = list.Insert(0, 12); foreach (var item in list) {
Console.WriteLine(item);
} // 12 11

ImmutableList<T> 可以索引,但是注意性能问题,不能用它来简单的替代 List<T>。它的内部实现是用的二叉树组织的数据,这么做是为了让不同的实例之间共享内存。

ImmutableHashSet

有些时候需要这样一个数据结构:不需要存放重复内容,不经常修改,可以被多个线程安全访问。时间复杂度 O(log N)。

var set = ImmutableHashSet<int>.Empty;
set = set.Add(11);
set = set.Add(12); foreach (var item in set) {
Console.WriteLine(item);
} // 11 12 顺序不定

线程安全字典

一个线程安全的键值对集合,多个线程读写仍然能保持同步。

ConcurrentDictionary

混合使用了细粒度的锁定和无锁技术,它是最实用的集合类型之一。

var dictionary = new ConcurrentDictionary<int, string>();
dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero");

如果多个线程读写一个共享集合,实用 ConcurrentDictionary<TKey,TValue> 是最合适的。如果不会频繁修改,那么更适合使用 ImmutableDictionary<TKey,TValue> 。

它最适合用于在需要共享数据的场合,即多个线程共享一个集合,如果一些线程只添加元素一些线程只移除元素,那最好使用 生产者/消费者集合(BlockingCollection<T>)。

初始化共享资源

程序多个地方使用一个值,第一次访问时对它进行初始化。

static int _simpleVluae;
static readonly Lazy<Task<int>> shardAsyncInteger =
new Lazy<Task<int>>(async () => {
await Task.Delay(2000).ConfigureAwait(false);
return _simpleVluae++;
}); public static void Main(string[] args) { int shareValue = shardAsyncInteger.Value.Result;
Console.WriteLine(shareValue); // 0
shareValue = shardAsyncInteger.Value.Result;
Console.WriteLine(shareValue); // 0
shareValue = shardAsyncInteger.Value.Result;
Console.WriteLine(shareValue); // 0
}

C# 并发编程 · 经典实例的更多相关文章

  1. 《C#并发编程经典实例》笔记

    1.前言 2.开宗明义 3.开发原则和要点 (1)并发编程概述 (2)异步编程基础 (3)并行开发的基础 (4)测试技巧 (5)集合 (6)函数式OOP (7)同步 1.前言 最近趁着项目的一段平稳期 ...

  2. 《C# 并发编程 · 经典实例》读书笔记

    前言 最近在看<C# 并发编程 · 经典实例>这本书,这不是一本理论书,反而这是一本主要讲述怎么样更好的使用好目前 C#.NET 为我们提供的这些 API 的一本书,书中绝大部分是一些实例 ...

  3. 《C#并发编程经典实例》学习笔记—2.3 报告任务

    问题 异步操作时,需要展示该操作的进度 解决方案 IProgress<T> Interface和Progress<T> Class 插一段话:读<C#并发编程经典实例&g ...

  4. [书籍]用UWP复习《C#并发编程经典实例》

    1. 简介 C#并发编程经典实例 是一本关于使用C#进行并发编程的入门参考书,使用"问题-解决方案-讨论"的模式讲解了以下这些概念: 面向异步编程的async和await 使用TP ...

  5. 《C#并发编程经典实例》学习笔记—2.7 避免上下文延续

    避免上下文延续 在默认情况下,一个 async 方法在被 await 调用后恢复运行时,会在原来的上下文中运行. 为了避免在上下文中恢复运行,可让 await 调用 ConfigureAwait 方法 ...

  6. 《C#并发编程经典实例》学习笔记—3.1 数据的并行处理

    问题 有一批数据,需要对每个元素进行相同的操作.该操作是计算密集型的,需要耗费一定的时间. 解决方案 常见的操作可以粗略分为 计算密集型操作 和 IO密集型操作.计算密集型操作主要是依赖于CPU计算, ...

  7. 《C#并发编程经典实例》学习笔记-关于并发编程的几个误解

    误解一:并发就是多线程 实际上多线程只是并发编程的一种形式,在C#中还有很多更实用.更方便的并发编程技术,包括异步编程.并行编程.TPL 数据流.响应式编程等. 误解二:只有大型服务器程序才需要考虑并 ...

  8. 并发编程概述--C#并发编程经典实例

    优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版.NET 中的程序库和语 ...

  9. 《C#并发编程经典实例》学习笔记-第一章并发编程概述

    并发编程的术语 并发 同时做多件事情 多线程 并发的一种形式,它采用多个线程来执行程序. 多线程是并发的一种形式,但不是唯一的形式. 并行处理 把正在执行的大量的任务分割成小块,分配给多个同时运行的线 ...

  10. c#并发编程经典实例文摘

    第1章 并发编程概述 1.1 并发编程简介 并发: 多线程(包括并行处理) 异步编程(异步操作)程序启动一个操作,而该操作将会在一段时间后完成 响应时编程(异步事件)可以没有一个实际的开始,可以在任何 ...

随机推荐

  1. 【GMT43智能液晶模块】基于HAL库的SDRAM和LCD驱动例程(MDK工程&CubeMX工程)

    说明: 1.该工程基于HAL库实现动态存储器SDRAM驱动以及液晶控制器LCD驱动. 2.工程通过STM32CubeMX(Version 4.22.0)配置生成,可直接打开进行配置. 3.KEIL M ...

  2. hive建表报错:Specified key was too long; max key length is 767 bytes,hadoophive

    情况描述 Hive建表时报错,元数据存储在mysql中. 报错信息 如下: FAILED: Execution Error, bytes com.mysql.jdbc.exceptions.jdbc4 ...

  3. c++中常用的泛型算法

    std中定义了很好几种顺序容器,它们自身也提供了一些操作,但是还有很多算法,容器本身没有提供. 而在algorithm头文件中,提供了许多算法,适用了大多数顺序容器.与c++11相比,很多函数在 c+ ...

  4. PXE(preboot execution environment):【网络】预启动执行环节:安装 ubuntu、rehat系列:成功

    必要条件: 网卡硬件支持 搭建相配套的DHCP\TFTP\HTTP(或FTP)后端服务器:推荐pxesrv,好用! 准备必要的pxe引导文件:pxelinux.0.menu.c32,该2位来自sysl ...

  5. 自己手写一个queuelink

    代码如下: <?php class QueueLink { private $first_key=0;//最前面的那个元素的键 private $first_value=0;//最前面的那个元素 ...

  6. Jquery EasyUI Combotree 初始化赋值

    Jquery EasyUI Combotree 初始化赋值 ================================ ©Copyright 蕃薯耀 2018年5月7日 https://www. ...

  7. miniprogrampatch 提供 watch 和 computed 特性

    推荐一个小程序补丁 github:miniprogrampatch,为你的 Page 和 Component 增加 watch 和 computed 特性. 安装 通过 npm 安装:npm inst ...

  8. windows服务器的误解

    自以为服务器就一种 配置,mac,windows服务器 目的:mac希望连接windows服务器,并替换打包的项目文件, 误区,使用ssh 最后明白了 直到看到一句话 阿里云ECS的安全组默认只放行2 ...

  9. ThinkPHP3.2.3框架下接入阿里云短信服务接口实现:注册登录

    首先介绍下短信注册登录流程: 注册页面点击获取手机号验证码按钮,用jquery的click事件POST或GET方法把手机号发送到后台控制器: 后台控制器创建函数,收到手机号后生成随机码,例如:6位的随 ...

  10. Ubuntu系统安装nginx

    1.首先查看linux系统 cat /proc/version Linux version 4.9.59-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (cr ...