Programming/C# - Window

C# / 동기(async), 비동기(await), 병렬(parallel) 처리

esoog Polaris 2023. 8. 4. 12:28
반응형

# 동기, 비동기, 그리고 병렬 처리

다양한 방식으로 작업을 처리하고 성능을 최적화하는 데 도움을 줍니다. 각각의 처리 방식을 비교하여 설명하겠습니다:

1. 동기 처리 (Synchronous):
   - 동기 처리는 작업이 순차적으로 실행되는 방식입니다. 즉, 한 작업이 완료되기를 기다린 다음 다음 작업을 수행합니다.
   - 일반적으로 단일 스레드에서 작업이 처리되며, 한 작업이 끝나야 다음 작업을 수행할 수 있습니다.
   - 동기 처리는 간단하고 예측 가능하지만, 대규모 또는 시간이 오래 걸리는 작업에서는 성능 문제가 발생할 수 있습니다.

2. 비동기 처리 (Asynchronous):
   - 비동기 처리는 작업이 병렬적으로 실행되는 방식으로, 다른 작업이 진행 중일 때 다른 작업을 시작할 수 있습니다.
   - 비동기 키워드인 `async`와 `await`를 사용하여 비동기 메서드를 작성합니다. `await`는 작업이 완료될 때까지 현재 스레드를 차단하지 않고 다른 작업을 수행할 수 있도록 해줍니다.
   - 비동기 처리는 I/O 작업이나 원격 서비스 호출과 같은 작업에서 주로 사용되며, 프로그램의 반응성을 향상시키고 성능을 향상시킬 수 있습니다.

 

* using System.Threading.Tasks;:

이 네임스페이스에는 `Task` 및 `Task<TResult>`와 같은 클래스, `async` 및 `await` 키워드와 함께 사용되는 비동기 프로그래밍의 핵심 요소가 포함되어 있습니다.


1. **Task 및 Task<TResult> 클래스**:
   - `Task` 클래스는 비동기 작업을 나타내는 클래스입니다. `Task<TResult>`는 결과를 반환하는 비동기 작업을 나타냅니다.
   - 이러한 클래스는 비동기 메서드에서 사용되며, 비동기 작업의 상태를 추적하고 작업을 관리하는 데 사용됩니다.
2. **async 및 await 키워드**:
   - `async` 키워드는 메서드를 비동기 메서드로 표시합니다. 비동기 메서드는 `await` 키워드를 사용하여 비동기 작업의 진행을 차단하지 않고 다른 작업을 수행할 수 있습니다.
   - `await` 키워드는 비동기 메서드 내에서 비동기 작업의 완료를 대기하는 데 사용됩니다. 작업이 완료되면 결과를 반환하거나, 예외를 throw하거나, 작업이 완료되지 않았다면 다른 일을 수행합니다.
3. **비동기 프로그래밍**:
   - 비동기 프로그래밍은 I/O 작업, 원격 서비스 호출, 데이터베이스 쿼리 및 기타 비동기 작업을 비차단 방식으로 처리하기 위해 사용됩니다.
   - 비동기 작업을 사용하면 응용 프로그램이 다른 작업을 수행하면서 I/O 또는 원격 작업을 효율적으로 처리할 수 있으며, 응답성을 향상시킬 수 있습니다.

 

 

* 예시코드

private async void button1_Click(object sender, EventArgs e) {
	Console.WriteLine("1");
    toolStripStatusLabel1.Text = "";
    await Task.Run(() => DoSomething());
    // await를 사용하면? Task 실행 결과 기다리고 다음 진행(1,2,3,4)
    // await를 안하면? 바로 스레드 기다리지 않고 바로 밑으로 진행(1,4,2,3)
    // 결과를 받고 진행할 것이냐? 아니냐? 관전에 따라 사용
    toolStripStatusLabel1.Text = "종료";
    Console.WriteLine("4");
}

// 반환값이 없는 동기 메서드
private void DoSomething() {
    Console.WriteLine("2");
    Thread.Sleep(1000); // 본래는 시간이 소요되는 처리
    Console.WriteLine("3");
}

private async void button2_Click(object sender, EventArgs e) {
    toolStripStatusLabel1.Text = "";
    var elapsed = await Task.Run(() => DoSomething2());
    toolStripStatusLabel1.Text = $"{elapsed}종료";
}

// 반환값이 있는 동기 메서드
private long DoSomething2() {
    var sw = Stopwatch.StartNew();
    Thread.Sleep(4000); // 본래는 시간이 소요되는 처리
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

private async void button3_Click(object sender, EventArgs e) {
    toolStripStatusLabel1.Text = "";
    await DoSomethingAsync();
    toolStripStatusLabel1.Text = "종료";
}


// 비동기 메서드 - DoSomethingAsync는 아무것도 반환하지 않는다
private async Task DoSomethingAsync() {
    await Task.Run(() => {
        Thread.Sleep(4000); // 본래는 시간이 소요되는 처리
    });
}


private async void button4_Click(object sender, EventArgs e) {
    toolStripStatusLabel1.Text = "";
    var elapsed = await DoSomethingAsync(4000);
    toolStripStatusLabel1.Text = $"{elapsed}소요시간";
}

// 비동기 메서드 - DoSomethingAsync는 long형 값을 반환한다
private async Task<long> DoSomethingAsync(int milliseconds) {
    var sw = Stopwatch.StartNew();
    await Task.Run(() => {
        Thread.Sleep(4000); // 본래는 시간이 소요되는 처리
    });
    sw.Stop();
    return sw.ElapsedMilliseconds;
}



3. 병렬 처리 (Parallel):
   - 병렬 처리는 여러 작업이 동시에 실행되는 방식입니다. 주로 CPU 집약적인 작업을 처리할 때 사용됩니다.
   - C#에서 병렬 처리를 위해 `Parallel` 클래스 및 LINQ의 `Parallel` 확장 메서드를 사용할 수 있습니다.
   - 병렬 처리는 다중 스레드 또는 다중 프로세서를 활용하여 성능을 향상시킬 수 있으며, 멀티코어 프로세서에서 특히 효과적입니다.

 

* 예시코드

/////////////////////////////////////////////////////// 메서드 정의 부분
// 5000번째 소수를 구한다
private static int GetPrimeAt5000() {
    return GetPrimes().Skip(4999).First();
}

// 6000번째 소수를 구한다
private static int GetPrimeAt6000() {
    return GetPrimes().Skip(5999).First();
}

// 위의 두 개의 메서드가 호출하는 하위 메서드
static IEnumerable<int> GetPrimes() {
    for (int i = 2; i < int.MaxValue; i++) {
        bool isPrime = true;
        for (int j = 2; j < i; j++) {
            if (i % j == 0) {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
            yield return i;
    }
}
/////////////////////////////////////////////////////// 메서드 정의 부분

// 비병렬 일반 버전
public void RunSync() {
    var sw = Stopwatch.StartNew();
    var prime1 = GetPrimeAt5000();
    var prime2 = GetPrimeAt6000();
    sw.Stop();
    Console.WriteLine(prime1);
    Console.WriteLine(prime2);
    Console.WriteLine($"실행 시간: {sw.ElapsedMilliseconds}밀리초");
}

// 병렬 버전
private async Task RunAsync() {
    var sw = Stopwatch.StartNew();
    var task1 = Task.Run(() => GetPrimeAt5000());
    var task2 = Task.Run(() => GetPrimeAt6000());
    var prime1 = await task1;
    var prime2 = await task2;
    sw.Stop();
    Console.WriteLine(prime1);
    Console.WriteLine(prime2);
    Console.WriteLine($"실행 시간: {sw.ElapsedMilliseconds}밀리초");
}

// 모든 태스크 끝날 때까지 배열형태로 병렬 처리
public async Task WhenAll() {
    var sw = Stopwatch.StartNew();
    var tasks = new Task<int>[] {
        Task.Run(() => GetPrimeAt5000()),
        Task.Run(() => GetPrimeAt6000()),
    };
    var results = await Task.WhenAll(tasks);
    foreach (var prime in results)
        Console.WriteLine(prime);
    Console.WriteLine($"실행 시간: {sw.ElapsedMilliseconds}밀리초");
}


간단한 예를 통해 설명하면, 동기 처리는 한 줄로 이어지는 작업의 흐름을 따르고, 비동기 처리는 I/O 대기 시간을 최소화하고 동시성을 지원하며, 병렬 처리는 CPU 집약적인 작업을 동시에 처리합니다. 여러 처리 방식은 프로그램의 성격과 요구 사항에 따라 선택되며, 종종 비동기 및 병렬 처리를 함께 사용하여 최상의 성능을 얻기도 합니다.

 

 

728x90

'Programming > C# - Window' 카테고리의 다른 글

C#/ SQL server 사용  (0) 2023.09.04
C#/ 보충 1(객체, 스레드, 컬렉션스)  (0) 2023.09.04
C#/ Invoke()  (0) 2023.07.07
C#/ mysql 연동  (0) 2023.06.30
C#/ 스레드(Thread)  (0) 2023.06.29