Home > Article > Backend Development > C# Concurrent Programming·Classic Examples Reading Notes
Preface
Recently I was reading the book "C# Concurrent Programming·Classic Examples". This is not a theoretical book, but it is a book that mainly talks about how to better use the current C#.NET A book of these APIs is provided for us. Most of the books are examples, which are often used in daily development.
I quite agree with some of the views in the book. For example, the author said that most books currently put content about concurrent multi-threading at the end, but lack an introductory guide and reference for concurrent programming.
Another point of view is that the vast majority of domestic technical personnel believe that the lower the level of technology, the more powerful it is, while those who do upper-level applications are "code farmers". The author opposes this view. In fact, we can make good use of existing Library is also a kind of ability. Although understanding the basic knowledge is still helpful in daily life, it is better to learn from higher-level abstract concepts.
Asynchronous basics
Task suspension and hibernation
To pause or hibernate tasks asynchronously, you can use Task.Delay();
static async Task<T> DelayResult<T>(T result, TimeSpan delay) { await Task.Delay(delay); return result; }
Asynchronous retry mechanism
A simple exponential backoff strategy, the retry time will increase gradually. This strategy is generally used when accessing Web services.
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); } }
Report progress
In asynchronous operations, it is often necessary to display the progress of the operation, and you can use IProcess
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"); }
Wait for a group of tasks
Execute several tasks at the same time, wait for them all to complete
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();
Wait for any one task to complete
Execute several tasks, only Requires a response to the completion of one of these. It is mainly used to perform multiple independent attempts on an operation. As long as one of the attempts is completed, the task is completed.
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; }
Collections
Immutable stacks and queues
Require a stack and queue that will not be modified frequently and can be safely accessed by multiple threads. Their API is very similar to Stack
In terms of internal implementation, when an object is overwritten (reassigned), the immutable collection returns a modified collection, and the original collection reference does not change. That is to say, if another If a variable refers to the same object, it (other variables) will not change.
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
In fact, the two stacks share the memory of storing 11. This implementation is very efficient, and each instance is thread-safe.
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
Immutable lists and collections
ImmutableList
Time complexity
Sometimes you need a data structure that supports indexing, is not modified frequently, and can be safely accessed by multiple threads.
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
ImmutableHashSet
Sometimes you need such a data structure: it does not need to store duplicate content, is not modified frequently, and can be safely accessed by multiple threads. Time complexity 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 顺序不定
Thread-safe dictionary
A thread-safe key-value pair collection, multiple threads can still maintain synchronization when reading and writing.
ConcurrentDictionary
Using a mixture of fine-grained locking and lock-free techniques, it is one of the most practical collection types.
var dictionary = new ConcurrentDictionary<int, string>(); dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero");
If multiple threads read and write a shared collection, ConcurrentDictionary
It is most suitable for situations where data needs to be shared, that is, multiple threads share a collection. If some threads only add elements and some threads only remove elements, it is best to use a producer/consumer collection ( BlockingCollection
Initialize shared resources
The program uses a value in multiple places and initializes it when accessing it for the first time.
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 }
The above is the content of C# Concurrent Programming·Classic Examples reading notes. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!