>  기사  >  백엔드 개발  >  C# 동시 프로그래밍·고전적인 예제 읽기 노트

C# 동시 프로그래밍·고전적인 예제 읽기 노트

黄舟
黄舟원래의
2017-02-06 16:43:461448검색

서문

최근에 "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 및 Process를 사용할 수 있습니다.

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 및 Queue와 매우 유사합니다. 성능 측면에서 불변 스택(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

시간 복잡성

C# 동시 프로그래밍·고전적인 예제 읽기 노트

때로는 인덱싱을 지원하고 자주 수정되지 않으며 여러 스레드에서 안전하게 액세스할 수 있는 데이터 구조가 필요합니다.

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는 색인을 생성할 수 있지만 성능 문제에 주의하고 단순히 List를 대체하는 데 사용할 수는 없습니다. 내부 구현에서는 이진 트리를 사용하여 데이터를 구성합니다. 이는 서로 다른 인스턴스 간에 메모리를 공유할 수 있도록 하기 위해 수행됩니다.

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 유틸리티가 가장 적합합니다. 자주 수정하지 않을 경우에는 ImmutableDictionary를 사용하는 것이 더 적합합니다.

데이터를 공유해야 하는 상황, 즉 여러 스레드가 컬렉션을 공유하는 경우에 가장 적합합니다. 일부 스레드는 요소를 추가만 하고 일부 스레드는 요소를 제거만 하는 경우 생산자/소비자를 사용하는 것이 가장 좋습니다. 컬렉션(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)를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.