Parallel : 단일 CPU를 사용했을 시절에는 CPU하나에 여러개의 Thread를 사용해서 처리했는데, 요즘에는 기본적으로 2~ 4개의 여러 CPU를 사용한다. 이러한
여러 CPU들을 골고루 사용하기 위해 .Net 4.0에서는 Parallel이라는 병렬 프로그래밍이 추가되었다.
1. Data Parallel : 각 CPU들에게 대량의 데이터를 병렬로 동시에 처리.
2. Task Parallel : 큰 작업을 Task로 분할해서 각 쓰레드들이 처리.
Parallel.Invoke() : 여러 작업들을 병렬로 처리, 다수의 작업 내용을 Action Delegate로 받아 다중 쓰레드들로 동시에 병렬 Task를 나눠서 실행.
※ TaskContinuationOptions
1. TaskContinuationOptions.None //선행작업이 어떤식으로 끝나든 이어서 실행하라는 옵션
2. TaskContinuationOptions.OnlyOnCanceled //선행작업이 취소가 되었을 때 이어서 실행하라는 옵션
3. TaskContinuationOptions.OnlyOnFaulted //선행작업이 Fault가 되었을 때 이어서 실행하라는 옵션
4. TaskContinuationOptions.OnlyOnRanToCompletion //선행작업이 성공했을 때 이어서 실행하라는 옵션
[사용법]
----------------------------------------------------------------
// Loops : 루프의 범위를 지정하여 병렬로 처리할 수 있다.
public void Start1()
{
var arr = Enumerable.Range(0, 10000);
// 기존
for (int i = 0; i < 100000; i++)
DoWork(i);
Thread.Sleep(5000);
// TPL
Parallel.For(0, 100000, (i) => DoWork(i));
Parallel.Invoke(
() => { DoWork1(); //임시 함수명 },
() => { DoWork2(); //임시 함수명 }
);
Console.ReadLine();
}
[예제 1]
----------------------------------------------------------------
// 한 작업이 어떤 상태로 끝났을 때, 상태에 따른 추가적인 작업을 하게 만드는 소스
기본 예제 소스
void Factory()
{
var displayData = Task.Factory.StartNew(() =>
{
}).ContinueWith((x) =>
{
}).ContinueWith((x) =>
{
});
}
----------------------------------------------------------------
public void Start5()
{
Task<string> task = new Task<string>(Calc, 1L);
task.Start();
Task<string> justDoIt = task.ContinueWith<string>(
(antecedentTask) =>
{
Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
return Calc(100001L);
});
}
// 선행작업이 어떤식으로 끝나든 이어서 실행하라는 옵션
TaskContinuationOptions.None);
// 선행작업이 처리안된 예외를 던지는 상태에서만 이어서 실행하라는 옵션
//TaskContinuationOptions.OnlyOnFaulted
Task<string> justDoIt2 = justDoIt.ContinueWith<string>(
(antecedentTask) =>
{
Console.WriteLine("이전 작업 상태 : {0}", antecedentTask.Status);
return Calc(200001L);
},
TaskContinuationOptions.None);
try
{
Console.WriteLine(task.Result);
}
catch (AggregateException ex)
{
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine("에러 : {0}", item.Message);
}
}
finally
{
try
{
Console.WriteLine(justDoIt.Result);
}
catch (AggregateException ex)
{
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine("에러 : {0}", item.Message);
}
}
finally
{
Console.WriteLine(justDoIt2.Result);
Console.WriteLine("끝");
}
}
}
[Task 사용법]
public void Start2()
{
const int max = 10000;
//현재 작업중인 스레드외에 추가로 스레드를 생성
Task task = new Task(() =>
{
for (int count = 0; count < max; count++)
{
Console.Write("|");
}
Console.Write("추가 쓰레드 끝");
});
//추가 스레드 시작
task.Start();
//현재 작업중인 스레드에서도 반복문 시작
for (int count = 0; count < max; count++)
{
Console.Write("-");
}
Console.Write("메인 쓰레드 끝");
//혹시 현재 스레드가 빨리 끝나더라도,
//추가 스레드가 끝날 때 까지 기다리기.
task.Wait();
//Wait메서드는 메인 스레드가 먼저 끝나더라도, 추가 스레드가 끝날 때까지 기다리게 하는 역할을 합니다. 그래서 메인 스레드가 빨리 끝나더라도, 항상 추가 스레드의 결과까지 제대로 출력되게 되는 것이죠.
Console.ReadLine();
}
[예외 처리 사용법]
----------------------------------------------------------------
public void Start4()
{
Task<string> task = new Task<string>(Calc, 1L);
task.Start();
try
{
Console.WriteLine(task.Result);
}
catch (AggregateException ex)
{
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine("에러 : {0}", item.Message);
}
}
Console.WriteLine(task.Result);
Console.ReadLine();
}
----------------------------------------------------------------
[다른 Thread 종료 요청]
----------------------------------------------------------------
// 한 스레드에서 다른 스레드 종료 요청
// 한 스레드가 다른 스레드를 강제로 종료시키는 게 아니라,
작업 취소 API를 통해서 작업을 취소해줄 것을 요청하는 것이죠.
(Cancel Token 사용)

// 취소 플래그를 통해서 취소요청을 받은 작업은 취소요청에 어떻게 응답할 것인지 선택할 수 있습니다.
int cnt = 0;
public void PrintDash(CancellationToken cancellationToken)
{
cancellationToken.Register(Canceled);
//강제 에러호출
if (cnt == 0)
throw new ApplicationException("그냥 에러가 났음");
while (!cancellationToken.IsCancellationRequested)
{
Console.Write("-");
}
}
public void Canceled()
{
Console.WriteLine("작업이 취소되었네.. (Cancel함수)");
}
public void Start6()
{
string stars = "*".PadRight(Console.WindowWidth - 1, '*');
CancellationTokenSource cancellationTokenSource =
new CancellationTokenSource();
Task task = Task.Factory.StartNew(
() => PrintDash(cancellationTokenSource.Token),
cancellationTokenSource.Token
);
//task가 에러 났을 때만 작업
Task faultTask = task.ContinueWith(
(x) =>
{
Console.WriteLine("에러남");
cnt = 1; PrintDash(cancellationTokenSource.Token);
},
TaskContinuationOptions.OnlyOnFaulted
);
//task가 취소 됬을 때만 작업
Task canceledTask = task.ContinueWith(
(antecedentTask) => Console.WriteLine("취소됨"),
TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
// cancellationTokenSource.Token = false : Cancel 함수 실행 전
cancellationTokenSource.Cancel();
Console.ReadLine();
Console.WriteLine(stars);
try
{
Console.WriteLine("작업의 완료상태 : {0}", task.Status);
Console.WriteLine(faultTask.Status);
Console.WriteLine(canceledTask.Status);
}
catch
{
}
Console.WriteLine();
Console.ReadLine();
}
[병렬 작업 & 병렬 처리 취소 방법]
----------------------------------------------------------------
public void Start7()
{
int[] nums = Enumerable.Range(1, 10000000).ToArray<int>();
//Task tt2 = Task.Factory.StartNew(() =>
//{
// Parallel.ForEach(nums, po2, (num) =>
// Console.WriteLine(num * num));
//});
CancellationTokenSource cts = new CancellationTokenSource();
ParallelOptions po = new ParallelOptions
{
CancellationToken = cts.Token
};
cts.Token.Register(
() => Console.WriteLine("cts에 등록된 함수")
);
try
{
Task task = Task.Factory.StartNew(
() =>
{
Parallel.For(0, 100, po,
(i) => Console.WriteLine("Num : " + i));
});
Task task2 = task.ContinueWith(
(x) =>
{
Console.WriteLine("정상처리");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
catch (OperationCanceledException ex)
{
}
//Console.ReadLine();
//cts.Cancel();
Console.ReadLine();
}
[LINQ 활용]
----------------------------------------------------------------
public int[] SimpleParallelTask(int[] source, CancellationToken token)
{
Func<int, int> square = (num) =>
{
Console.WriteLine(Task.CurrentId);
return num * num * num * num * num * num * num * num * num * num * num * num * num * num
* num * num * num * num * num * num * num * num * num * num;
};
return source.AsParallel()
//병행으로 처리할 쓰레드 개수
.WithDegreeOfParallelism(4)
.WithCancellation(token)
.Select(square)
.ToArray();
}
'- 개발 > C# 개발정리' 카테고리의 다른 글
C# Boxing, UnBoxing, UpCasting, DownCasting 간단 설명 질문 (1) | 2023.12.27 |
---|---|
[C# Async/await/Invoke/beginInvoke/크로스스레딩] 비동기 처리 방법 /예제소스 (1) | 2023.12.26 |