1. 스레드를 이용한 비동기 처리


1
2
3
4
5
6
7
8
9
10
11
12
private void Button_Click(object sender, RoutedEventArgs e)
{
    label.Content = " ";
    var th = new Thread(DoSomething);
    th.Start();
}
 
private void DoSomething()
{
    DoLongTimeWork();
    Dispatcher.BeginInvoke((Action)delegate() { label.Content = "종료"; });
}
cs


Thread 클래스의 생성자로 비동기로 실행할 메서드를 지정한 후 스레드를 Start 시켜준다.

폼 위에 있는 컨트롤은 UI스레드가 아니면 받아들이지 않으므로 BeginInvoke 메서드로 UI 스레드 상에서 실행시킴.




2. BackgroudWorker를 사용한 비동기 처리


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private BackgroundWorker _worker = new BackgroundWorker();
 
public MainWindow()
{
    InitializeComponent();
    _worker.DoWork += _worker_DoWork;
    _worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
    _worker.ProgressChanged += _worker_ProgressChanged;
    _worker.WorkerReportsProgress = true;
}
 
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
    var collection = Enumerable.Range(1200).ToArray();
    int count = 0;
    foreach (var n in collection)
    {
        var per = count * 100 / collection.Length;
        _worker.ReportProgress(per, null);
 
        count++;
        Thread.Sleep(100);
    }
}
 
private void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    progressBar.Value = progressBar.Maximum;
    label.Content = "종료";
}
 
private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = e.ProgressPercentage;
}
 
private void Button_Click(object sender, RoutedEventArgs e)
{
    label.Content = " ";
    _worker.RunWorkerAsync();
}
cs


이벤트를 사용해 비동기 처리를 구현할 수 있음.

RunWorkerAsync() 메서드가 호출되면 DoWork에 등록된 이벤트가 비동기로 실행된다.




3. Task를 이용한 비동기 처리


1
2
3
4
5
private void Button_Click(object sender, RoutedEventArgs e)
{
    label.Content = " ";
    Task.Run(() => DoSomething());
}
cs




4. async await을 이용한 비동기 프로그래밍


1
2
3
4
5
6
private async void Button2_Click(object sender, RoutedEventArgs e)
{
    label.Content = " ";
    await Task.Run(() => DoSomething());
    label.Content = "종료";
}
cs


async로 수식된 메서드는 비동기 메서드가 되어 이 안에서만 await을 사용할 수 있다.

await 키워드는 지정한 Task가 끝날 때까지 처리를 중단하고 지정한 Task가 끝나면 이후의 처리를 계속함.




5. 비동기 메서드를 정의


1
2
3
4
5
6
7
8
9
10
11
12
13
14
private async Task<long> DoSomethingAsync(int milliseconds)
{
    var sw = Stopwatch.StartNew();
    await Task.Run(() => DoLongTimeWork());
    sw.Stop();
    return sw.ElapsedMilliseconds;
}
 
private async void Button2_Click(object sender, RoutedEventArgs e)
{
    label.Content = " ";
    var elapsed = await DoSomethingAsync(4000);
    label.Content = $"{elapsed}초";
}
cs


DoSomething 메서드 자체를 비동기로 만들 수도 있다. 

리턴값이 없다면 반환형을 Task. 있다면 Task<Result>형으로 지정해야한다.

task를 리턴하므로 버튼클릭에서는 그대로 await 메서드로 사용하면 된다.




6. PLINQ를 통한 병렬 처리

Parallel LINq(PLINQ)를 사용하면 LINQ의 쿼리 처리를 쉽게 병렬화할 수 있음.


1
2
3
4
5
var selected = books.AsParallel()
    .WithDegreeOfParallelism(16)
    .AsOrdered()
    .Where(b => b.Price > 500 && b.Pages > 400)
    .Select(b => new { b.Title });
cs


AsParallel 메서드로 병렬화 할 수 있지만 순서를 보장할 수 없으므로

AsOrdered 메서드를 호출하면 순서를 보장할 수 있다. 다만 성능은 떨어짐.


- 병렬화할 하나하나의 처리에 시간이 오래 걸리거나

- 데이터 소스의 양이 많을 경우


에만 병렬화를 하자.


WithDegreeOfParallelism 메서드는 동시 실행되는 태스크 수를 제한 할 수 있다. 

코어가 24개 있다면 16개만 사용하고 8개는 다른 작업을 위해 남겨둘 수 있음.




7. Task를 이용한 병렬 처리


1
2
3
4
var task1 = Task.Run(() => GetPrimeAt5000());
var task2 = Task.Run(() => GetPrimeAt6000());
var prime1 = await task1;
var prime2 = await task2;
cs


그냥 2개의 메서드를 병렬로 호출하고 끝날때까지 기다린다.


1
2
var task1 = await Task.Run(() => GetPrimeAt5000());
var task2 = await Task.Run(() => GetPrimeAt6000());
cs


이런식으로 호출하면 병렬 처리가 되지않고 하나씩 처리되므로 주의해야한다.


1
2
3
4
5
6
7
var tasks = new Task<int>[]
{
    Task.Run(() => GetPrimeAt5000()),
    Task.Run(() => GetPrimeAt6000()),
};
 
var results = await Task.WhenAll(tasks);
cs


병렬하는 개수가 많다면 Task.WhenAll을 사용하여 각 반환값을 배열 형태로 받을 수 있다.


Posted by misty_
,