前言
最近在看《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 11int 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 12int nextItem;queue = queue.Dequeue(out nextItem);Console.WriteLine(nextItem); //output: 11
不可變列表和集合
ImmutableList
時間複雜度
有些時候需要這樣一個資料結構:支援索引,不經常修改,可以被多安全執行緒的訪問。
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#並發編程·經典執行個體讀書筆記的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!