방프리
23.11.08 C# 동시성 프로그래밍 (병렬 처리) 본문
1. 데이터의 병렬처리
- 병렬로 여러 데이터를 반복문을 통해 처리할 때
- 만약 공유하는 변수 혹은 리소스가 있다면 잠금(lock)을 사용해보는 것도 좋다.
- 책에서는 ForEach 위주로 설명하였지만 순차처리를 목적으로 한다면 ForEachAsync도 고려해볼만 하다.
void RotateMatrices(IEnumerable<Matrix> matrices, float degrees,
CancellationToken token)
{
Parallel.ForEach(matrices,
new ParallelOptions { CancellationToken = token },
matrix => matrix.Rotate(degress));
}
2. 병렬 집계
- 병렬 작업이 끝난 후 결과를 집계할 때
int ParallelSum(IEnumerable<int> values)
{
object mutex = new object();
int result = 0;
Parallel.ForEach(source: values,
localInit: () => 0,
body: (item, state, localValue) => localValue + item,
localFinally: localValue =>
{
lock (mutex)
{
result += localValue;
}
});
return result;
}
//위의 집계를 PLINQ를 통해 한 줄로 요약
int ParallelSum(IEnumerable<int> values)
{
return values.AsParallel().Sum();
}
//기본 지원 집계가 아닌 제네릭 형식의 집계를 원한다면
int ParallelSUm(IEnumerable<int> values)
{
return values.AsParallel().Aggregate(
seed: 0,
func: (sum, item) => sum + item
}
3. 병렬 호출
- 독립적인 여러 개의 메서드를 병렬로 호출할 때
- CancellationToken을 통해 중간 취소도 지원이 가능하다.
void DoAction20Times(Action action, CancellationToken token)
{
Action[] actions = Enumerable.Repeat(action, 20).ToArray();
Parallel.Invoke(new ParallelOptions { CancellationToken = token }, actions);
}
4. 동적 병렬 처리
- 작업의 구조와 개수에 따라 서로 다르게 적용
- 서로 연관이 있는 작업들 중 선, 후가 나뉘는 작업이라면 TaskCreationOptions.AttachedToParent
- 연속 작업을 사용해서 바로 다른 작업의 예약을 해야한다면 Task.COntinueWith
// 두 작업 A와 B는 부모,자식 관계를 가진다
void Traverse(Node current)
{
DoExpensiveActionOnNode(current);
if (current.Left != null)
{
Task.Factory.StartNew(
() => Traverse(current.Left),
CancellationTOken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
}
if (current.Right != null)
{
Task.Factory.StartNew(
() => Traverse(current.Right),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
}
}
void ProcessTree(Node root)
{
Task task = Task.Factory.StartNew(
() => Traverse(root),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
task.Wait();
}
// 두 작업 A와 B는 연속 작업 관계를 가진다
void ContinueTask()
{
Task task = Task.Factory.StartNew(
() => Thread.Sleep(TimeSpan.FromSecond(2)),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
Task continuation = task.ContinueWith(
t => Trace.WriteLine("Task is done"),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
5. PLINQ
- 기본적으로 Parallel이 좀 더 프로세스에 친화적이지만 PLINQ의 표현의 간결함과 사용 편의성을 고려한다면 LINQ보다
성능이 더 뛰어남
IEnumerable<int> MultiplyBy2(IEnumerable<int> values)
{
return values.AsParalle().Select(value => value * 2);
}
IEnumerable<int> MultiplyBy2(IEnumerable<int> values)
{
return values.AsParallel().AsOrderd().Select(value => value * 2);
}
int ParallelSum(IEnumerable<int> values)
{
return values.AsParallel().Sum();
}
'C# > 동시성 처리' 카테고리의 다른 글
23.11.19 C# 동시성 프로그래밍 (테스트) (0) | 2023.11.19 |
---|---|
23.11.12 C# 동시성 프로그래밍 (Reactive) (1) | 2023.11.12 |
23.11.11 C# 동시성 프로그래밍 (TPL) (0) | 2023.11.11 |
23.11.07 C# 동시성 프로그래밍 (비동기 스트림) (0) | 2023.11.07 |
23.11.05 C# 동시성 프로그래밍 (기초 기능) (0) | 2023.11.05 |
Comments