Task 클래스
System.Threading.Tasks 네임스페이스의 Task 클래스는 C#에서 비동기 코드를 쉽게 작성할 수 있도록 도와준다.
동기 코드는, 검사가 검으로 공격할 때처럼 동작한다. 검사가 검으로 상대를 찌른 뒤에 다시 뽑아야 칼을 쓸 수 있는 것처럼, 동기코드는 메서드를 호출한 뒤에 이 메서드의 실행이 완전히 종료(반환)되어야만 다음 메서드를 호출할 수 있다.
비동기 코드는, 궁수가 활을 쏠 때처럼 동작한다. 궁수는 화살을 쏘고 나면 바로 다음 화살을 쏠 준비를 할 수 있다. 이미 쏜 화살에 대해서는 잊고서 말이다. 즉,
비동기 코드는 궁수가 화살을 쏘는 것처럼 메서드를 호출한 뒤에 메서드의 종료를 기다리지 않고 바로 다음 코드를 실행한다. 작업을 시작한 후 결과를 기다리지 않고 다른 작업을 계속 수행할 수 있는 방식인 것이다.
따라서 인터넷에서 큰 파일을 다운로드하는 동안 다른 작업을 할 수 있도록 하거나, 복잡한 계산을 백그라운드에서 수행하면서 사용자 인터페이스가 멈추지 않도록 할 수 있다.
Task 클래스는 C#에서 비동기 프로그래밍을 위한 핵심적인 클래스이며, 다양한 기능을 제공하여 비동기 작업을 효율적으로 관리할 수 있도록 해준다.
Task 클래스의 주요 기능
비동기 작업 생성: Task 클래스의 생성자 또는 Task.Run() 메서드를 사용하여 비동기 작업을 생성할 수 있다.
(Task 클래스는 인스턴스를 생성할 때 Action 대리자를 인수로 넘겨받는다. 즉 Task() 생성자의 인수로, 반환형을 갖지 않는 메서드, 익명 메서드, 무명 함수 등을 넘겨받는다.)
작업 시작: Start() 메서드를 사용하여 비동기 작업을 시작할 수 있다.
결과 대기: Wait() 메서드를 사용하여 비동기 작업이 완료될 때까지 기다릴 수 있다.
결과 가져오기: Result 프로퍼티를 사용하여 비동기 작업의 결과를 가져올 수 있다.
예외 처리: Exception 프로퍼티를 사용하여 비동기 작업에서 발생한 예외를 처리할 수 있다.
취소: CancellationToken을 사용하여 비동기 작업을 취소할 수 있다.
연속: ContinueWith() 메서드를 사용하여 비동기 작업이 완료된 후 실행할 작업을 지정할 수 있다.
Task 클래스 사용 예시
using System;
using System.Threading;
using System.Threading.Tasks;
class TaskTest
{
static void Main(string[] args)
{
Task task = new Task(() =>
{
Thread.Sleep(1000); // 1초 동안 대기
Console.WriteLine("비동기 작업 완료!");
});
task.Start(); // 작업 시작
Console.WriteLine("메인 스레드 계속 실행...");
task.Wait(); // 작업 완료 대기
Console.WriteLine("모든 작업 완료!");
}
}
Task 클래스의 장점
병렬 처리, 비동기 처리, 멀티 스레드
병렬 처리, 비동기 처리, 멀티 스레드는 모두 프로그램의 성능을 향상시키는 데 사용되는 기술이지만, 각각의 개념과 적용 방식은 다르다.
병렬 처리
Parallel 클래스: for, foreach 루프를 병렬로 실행할 수 있도록 지원한다.Task 클래스: 작업을 비동기적으로 실행하고 결과를 가져올 수 있도록 지원한다.PLINQ: LINQ 쿼리를 병렬로 실행할 수 있도록 지원한다.비동기 처리
async/await 키워드: 비동기 메서드를 정의하고 호출할 수 있도록 지원한다.Task 클래스: 작업을 비동기적으로 실행하고 결과를 가져올 수 있도록 지원한다.멀티 스레드
Thread 클래스는 여러 작업을 나누지 않고 각각 처리한다. 하나의 프로세스 내에서 여러 개의 스레드를 생성하여 작업을 동시에 처리하는 것이다.System.Threading.Tasks 네임스페이스의 클래스들은 하나의 작업을 쪼갠 뒤 쪼개진 작업들을 동시에 처리하는 코드와 비동기 코드를 위해 설계되었다.)Thread 클래스로도 하나의 작업을 쪼갠 뒤 쪼개진 작업들을 동시에 처리하는 코드와 비동기 코드를 작성할 수는 있지만(System.Threading.Tasks의 클래스들도 내부적으로는 Thread로 구현되었기 때문), 쉽지 않다.Thread 클래스: 스레드를 생성하고 관리할 수 있도록 지원한다.ThreadPool 클래스: 스레드 풀을 사용하여 스레드를 효율적으로 관리할 수 있도록 지원한다.세 가지 기술의 관계
요약
병렬 처리, 비동기 처리, 멀티 스레드는 각각 다른 목적과 적용 방식을 가진 기술이다. 이러한 기술들을 적절히 활용하면 프로그램의 성능과 응답성을 향상시킬 수 있다.
예제
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace UsingTask
{
class MainApp
{
static void Main(string[] args)
{
string srcFile = args[0]; // 첫 번째 명령줄 인수를 srcFile에 저장한다.
// 파일 복사 작업을 수행하는 Action<object> 델리게이트를 생성한다.
Action<object> FileCopyAction = (object state) =>
{
String[] paths = (String[])state; // state 객체를 string 배열로 변환한다.
File.Copy(paths[0], paths[1]); // paths[0]에서 paths[1]로 파일을 복사한다.
// Task ID, Thread ID, 소스 파일 경로, 대상 파일 경로를 출력한다.
Console.WriteLine("TaskID:{0}, ThreadID:{1}, {2} was copied to {3}",
Task.CurrentId, Thread.CurrentThread.ManagedThreadId,
paths[0], paths[1]);
};
// FileCopyAction 델리게이트를 사용하여 Task 객체를 생성한다.
Task t1 = new Task(FileCopyAction, new string[] { srcFile, srcFile + ".copy1" });
// Task.Run() 메서드를 사용하여 Task 객체를 생성한다.
Task t2 = Task.Run(() =>
{
FileCopyAction(new string[] { srcFile, srcFile + ".copy2" });
});
t1.Start(); // t1 Task를 시작한다.
// FileCopyAction 델리게이트를 사용하여 Task 객체를 생성한다.
Task t3 = new Task(FileCopyAction, new string[] { srcFile, srcFile + ".copy3" });
t3.RunSynchronously(); // t3 Task를 동기적으로 실행한다.
t1.Wait(); // t1 Task가 완료될 때까지 기다린다.
t2.Wait(); // t2 Task가 완료될 때까지 기다린다.
t3.Wait(); // t3 Task가 완료될 때까지 기다린다.
// List<int>를 반환하는 Task<List<int>> 객체를 생성한다.
var myTask = Task<List<int>>.Run(
() =>
{
Thread.Sleep(1000); // 1초 동안 스레드를 일시 중지한다.
List<int> list = new List<int>(); // List<int> 객체를 생성한다.
list.Add(3); // list에 3, 4, 5를 추가한다.
list.Add(4);
list.Add(5);
return list; // list를 반환한다.
}
);
myTask.Wait(); // myTask가 완료될 때까지 기다린다.
}
}
}
코드 설명
이 C# 코드는 Task 클래스를 사용하여 비동기 작업을 수행하는 방법을 보여주는 예제이다.
세 개의 Task를 이용해서 세 개의 파일을 복사하는 프로그램 코드로, 첫 번째와 두 번째 Task는 비동기로 파일을 복사하고, 세 번쨰 Task는 동기로 파일을 복사한다.
FileCopyAction 델리게이트는 파일 복사 작업을 수행하고, Task 객체는 FileCopyAction 델리게이트를 비동기적으로 실행한다.
string srcFile = args[0];: 첫 번째 명령줄 인수를 srcFile에 저장한다.Action<object> FileCopyAction = (object state) => { ... };: 파일 복사 작업을 수행하는 Action<object> 델리게이트를 생성한다.Task t1 = new Task(FileCopyAction, new string[] { srcFile, srcFile + ".copy1" });: FileCopyAction 델리게이트를 사용하여 Task 객체를 생성한다.Task t2 = Task.Run(() => { ... });: Task.Run() 메서드를 사용하여 Task 객체를 생성한다.t1.Start();: t1 Task를 시작한다.Task t3 = new Task(FileCopyAction, new string[] { srcFile, srcFile + ".copy3" });: FileCopyAction 델리게이트를 사용하여 Task 객체를 생성한다.t3.RunSynchronously();: t3 Task를 동기적으로 실행한다.t1.Wait();, t2.Wait();, t3.Wait();: 각 Task가 완료될 때까지 기다린다.var myTask = Task<List<int>>.Run(() => { ... });: List<int>를 반환하는 Task<List<int>> 객체를 생성한다.myTask.Wait();: myTask가 완료될 때까지 기다린다.출력 결과
TaskID:1, ThreadID:3, test.txt was copied to test.txt.copy1
TaskID:2, ThreadID:4, test.txt was copied to test.txt.copy2
TaskID:3, ThreadID:1, test.txt was copied to test.txt.copy3
참고
Task 클래스는 비동기 작업을 나타낸다.Task.Run() 메서드는 Task를 비동기적으로 실행한다.RunSynchronously() 메서드는 Task를 동기적으로 실행한다.Wait() 메서드는 Task가 완료될 때까지 기다린다.Task<TResult> 클래스는 결과 값을 반환하는 Task를 나타낸다.