Parallel Programming (TPL)

2022. 2. 15. 00:00CSharp/Advance

반응형

TPL (Task Parallel Library)

다중 스레드 및 병렬 코드를 작성 하기 위한 .NET 권장 Library

애플리케이션에 병렬 처리 및 동시성 기능을 추가하는 과정을 단순화하여

개발자가 더 생산적으로 작업할 수 있도록 함

 

기본 사용법

Thread.CurrentThread.Name = "new Task";
Task taskA = new Task(() => Console.WriteLine("Hello from taskA."));
taskA.Start();
Console.WriteLine("Hello from thread '{0}'.",Thread.CurrentThread.Name);
taskA.Wait();
// output
// Hello from thread 'new Task'.
// Hello from taskA

Task.Run  을 통해  한 번에 작업을 만들고 시작할 수도 있다.

Thread.CurrentThread.Name = "Task.Run";
Task taskA = Task.Run(() => Console.WriteLine("Hello from taskA."));
Console.WriteLine("Hello from thread '{0}'.",Thread.CurrentThread.Name);
taskA.Wait();
// output
// Hello from thread 'Task.Run'.
// Hello from taskA

TaskFactory.StartNew 메서드를 사용하여 한 번에 작업을 만들고 시작할 수도 있다.

Thread.CurrentThread.Name = "Task.Factory.StartNew";

Task taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));
Console.WriteLine("Hello from thread '{0}'.",Thread.CurrentThread.Name);
taskA.Wait();
// output
// Hello from thread 'Task.Factory.StartNew'.
// Hello from taskA.

Task 는 Return 값을 받을 수 있다. 

private string HelloWorld() => "Hello World!";

public void TaskReturn()
{
    Thread.CurrentThread.Name = "TaskReturn";
    var taskA = Task.Factory.StartNew(() => HelloWorld());
    Console.WriteLine("Hello from thread '{0}'.", Thread.CurrentThread.Name);
    Console.WriteLine(taskA.Result);
    // output
    // Hello from thread 'TaskReturn'.
    // Hello World!
}

lambda 식을 사용 할 때는 capture scope 을 신경쓰자

각 반복 후에 변경되는 값이 아니라 변수의 참조만 캡처한다. 

Thread.CurrentThread.Name = "TaskCaptureScopeMistake";
Console.WriteLine("Hello from thread '{0}'.", Thread.CurrentThread.Name);
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    tasks.Add(Task.Factory.StartNew(() =>
                {
                    Console.WriteLine($"i = [{i}]");
                }));
}
// output
/*
    Hello from thread 'TaskCaptureScopeMistake'.
    i = [3]
    i = [3]
    i = [4]
    i = [4]
    i = [5]
    i = [5]
    i = [6]
    i = [9]
    i = [9]
    i = [9]
 */

정확한 내용을 캡쳐 하려면 다음과 같이 한다. 

Thread.CurrentThread.Name = "TaskCaptureScopeSolution";
Console.WriteLine("Hello from thread '{0}'.", Thread.CurrentThread.Name);
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    int j = i;
    tasks.Add(Task.Factory.StartNew(() =>
    {
        Console.WriteLine($"i = [{j}]");
    }));
}
Task.WaitAll(tasks.ToArray());
// output
/*
    Hello from thread 'TaskCaptureScopeSolution'.
    i = [0]
    i = [2]
    i = [1]
    i = [6]
    i = [5]
    i = [4]
    i = [3]
    i = [7]
    i = [9]
    i = [8]
 */

연속작업 만들기 

Task.ContinueWith 및 Task<TResult>.ContinueWith 메서드를 사용하면 선행 작업이 완료될 때 시작할 작업을 지정할 수 있다. 연속 작업의 대리자는 선행 작업에 대한 참조를 전달받아 선행 작업의 상태를 검사하고 Task<TResult>.Result 속성의 값을 검색하여 선행 작업의 출력을 입력으로 사용할 수 있다.

// 기본사용
Task task1 = Task.Factory.StartNew(() => Console.Write("Hello.."));
Task task2 = task1.ContinueWith(task => Console.Write("..world!"));
// output
// Hello....world!

// 이전 작업에서 throw 된 error 를 continuewith 와 함께 capture
Task task1 = Task.Factory.StartNew(() => throw new Exception("error fired"));
Task task2 = task1.ContinueWith(task => Console.Write("error capture" + task.Exception));

task2.Wait();

조건부 연속작업

기본적으로 연속 작업은 선행 작업이 완료되거나, 예외가 발생하거나, 취소되든 상관 없이 무조건 예약된다. TaskContinuationOptions 열거형에 포함된 (결합 가능한) 플래그 집합을 통해 이 동작을 변경할 수 있다.

 

Task t1 = Task.Factory.StartNew (...);
 
Task fault = t1.ContinueWith (ant => Console.WriteLine ("fault"),
                              TaskContinuationOptions.OnlyOnFaulted);
 
Task t3 = fault.ContinueWith (ant => Console.WriteLine ("t3"));

참조 :&amp;amp;nbsp;http://www.albahari.com/threading/part5.aspx#_Creating_and_Starting_Tasks

 

 

 

 

관련영상

https://youtu.be/QJNanhZo0IA

 

 

반응형