서문
최근에 "C# 동시 프로그래밍·클래식 예제"라는 책을 읽었습니다. 이 책은 이론적인 책이 아니라 현재의 C#.NET을 더 잘 활용하는 방법을 주로 이야기하는 책입니다. 이러한 API는 우리에게 제공됩니다. 대부분의 책은 일상적인 개발에 자주 사용되는 예제입니다.
책에 나오는 일부 견해는 상당히 동의합니다. 예를 들어 저자는 현재 책의 대부분이 동시 멀티스레딩에 대한 내용을 마지막에 담고 있지만 동시 프로그래밍에 대한 소개 가이드와 참고 자료가 부족하다고 말했습니다. .
또 다른 관점은 국내 기술 인력의 대다수가 기술 수준이 낮을수록 강력하다고 믿는 반면, 상위 수준의 응용을 하는 사람들은 '코드 파머'라는 것이다. 사실 기존의 라이브러리를 잘 활용하는 것도 일종의 능력이다. 기본적인 지식을 이해하는 것도 일상생활에 도움이 되지만, 좀 더 높은 수준의 추상적인 개념을 배우는 것이 더 좋다.
비동기 기본
작업 일시 중지 및 최대 절전 모드
작업을 비동기적으로 일시 중지하거나 최대 절전 모드로 전환하려면 Task.Delay();
static async Task<T> DelayResult<T>(T result, TimeSpan delay) { await Task.Delay(delay); return result; }
비동기 재시도 메커니즘을 사용할 수 있습니다.
간단한 지수 백오프 전략으로, 재시도 시간이 점차 증가합니다. 이 전략은 일반적으로 웹 서비스에 액세스할 때 사용됩니다.
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
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
내부 구현에서 객체를 덮어쓰면(재할당) 불변 컬렉션이 수정된 컬렉션을 반환하고 원래 컬렉션 참조는 변경되지 않습니다. 즉, 다른 변수가 동일한 것을 참조하는 경우. 객체(다른 변수)는 변경되지 않습니다.
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
시간 복잡성
때로는 인덱싱을 지원하고 자주 수정되지 않으며 여러 스레드에서 안전하게 액세스할 수 있는 데이터 구조가 필요합니다.
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
때때로 이러한 데이터 구조가 필요합니다. 중복된 콘텐츠를 저장할 필요가 없고 자주 수정되지 않으며 여러 스레드에서 안전하게 액세스할 수 있습니다. 시간 복잡도 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
데이터를 공유해야 하는 상황, 즉 여러 스레드가 컬렉션을 공유하는 경우에 가장 적합합니다. 일부 스레드는 요소를 추가만 하고 일부 스레드는 요소를 제거만 하는 경우 생산자/소비자를 사용하는 것이 가장 좋습니다. 컬렉션(BlockingCollection
공유 리소스 초기화
프로그램은 여러 위치에서 값을 사용하고 처음 액세스할 때 초기화합니다.
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# 동시 프로그래밍·고전 예제 읽기 노트 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!