前言

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

书中一些观点还是比较赞同,比如作者说目前绝大多数的图书对关于并发多线程等这些内容放到最后,而缺少一本介绍并发编程的入门指引和参考。另外一个观点是绝大多数国内的技术人员认为技术越底层就牛逼,而做上层应用的就是“码农”,作者反对了这一观点,其实能利用好现有的库也是一种能力,虽然说理解基础知识对日常生活仍然有帮助,但最好从更高级的抽象概念来学习。

异步基础

任务暂停,休眠

异步方式暂停或者休眠任务,可以使用 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
}

本文地址:http://www.cnblogs.com/savorboard/p/csharp-concurrency-cookbook.html

作者博客:Savorboard

欢迎转载,请在明显位置给出出处及链接

《C# 并发编程 · 经典实例》读书笔记的更多相关文章

  1. csapp读书笔记-并发编程

    这是基础,理解不能有偏差 如果线程/进程的逻辑控制流在时间上重叠,那么就是并发的.我们可以将并发看成是一种os内核用来运行多个应用程序的实例,但是并发不仅在内核,在应用程序中的角色也很重要. 在应用级 ...

  2. CSAPP 读书笔记 - 2.31练习题

    根据等式(2-14) 假如w = 4 数值范围在-8 ~ 7之间 2^w = 16 x = 5, y = 4的情况下面 x + y = 9 >=2 ^(w-1)  属于第一种情况 sum = x ...

  3. CSAPP读书笔记--第八章 异常控制流

    第八章 异常控制流 2017-11-14 概述 控制转移序列叫做控制流.目前为止,我们学过两种改变控制流的方式: 1)跳转和分支: 2)调用和返回. 但是上面的方法只能控制程序本身,发生以下系统状态的 ...

  4. CSAPP 并发编程读书笔记

    CSAPP 并发编程笔记 并发和并行 并发:Concurrency,只要时间上重叠就算并发,可以是单处理器交替处理 并行:Parallel,属于并发的一种特殊情况(真子集),多核/多 CPU 同时处理 ...

  5. 读书笔记汇总 - SQL必知必会(第4版)

    本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...

  6. 读书笔记--SQL必知必会18--视图

    读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...

  7. 《C#本质论》读书笔记(18)多线程处理

    .NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...

  8. C#温故知新:《C#图解教程》读书笔记系列

    一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...

  9. C#刨根究底:《你必须知道的.NET》读书笔记系列

    一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...

  10. Web高级征程:《大型网站技术架构》读书笔记系列

    一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...

随机推荐

  1. 模拟键盘发送文字(使用SendInput函数)

    嗯...老生常谈的话题, 不过系统的总结了一下, 找了个相对简单的实现方式, 可以方便的发送任何文字 参考另一片文章: http://www.cnblogs.com/-clq/archive/2011 ...

  2. <未测>源码升级安装glibc和rpm升级glibc

    源码升级安装glibc和rpm升级glibc http://jacklin9.spaces.live.com/blog/cns!A891B52E1182AFB2!346.entry http://bl ...

  3. bzoj3505

    ans=C((n+1)*(m+1),3)-三点一线的情况横线竖线我们可以先去掉然后考虑斜线,由于对称性我们只要考虑斜率大于0的即可有一个很显然的结论,但两点坐标差为x,y时,这条线段上的点数为gcd( ...

  4. 网络流(最大密集度子图,分数规划):UvaLive 3709 Hard Life

    John is a Chief Executive Officer at a privately owned medium size company. The owner of the company ...

  5. 【中途相遇法】【STL】BAPC2014 K Key to Knowledge (Codeforces GYM 100526)

    题目链接: http://codeforces.com/gym/100526 http://acm.hunnu.edu.cn/online/?action=problem&type=show& ...

  6. 关于DocumentCompleted事件

    关于DocumentCompleted事件,MSDN给出的解释是在文档加载完毕后执行,但是在我的程序中DocumentCompleted却被多次调用,查了一下资料,大概出现了以下几种情况. 1.Web ...

  7. 【转】C/C++ 内存对齐

    [转自]http://wenku.baidu.com/view/8eaaa26925c52cc58bd6bed2.htmlC/C++内存对齐 一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都 ...

  8. 我的学习笔记之node----node.js+socket.io实时聊天(1) (谨此纪念博客开篇)

    本想着从hello word开篇,也确实写了相关学习笔记.各种原因吧,现在又着急写出作品,便作罢. 这里将记录一个node.js+socket.io的实时聊天程序.(当然我也是跟着网上各种教程资料学习 ...

  9. WEB性能测试:你应该带上VisualStudio2010

    原文地址:http://www.16aspx.com/Article/62 在Web性能测试方面,增加了循环(Loops)和条件(Conditions),让开发人员可以为他们的应用程序写出更复杂,更智 ...

  10. zoj 1372

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1372 #include<iostream> #include& ...