Task에 대해서
Task 클래스
- Thread, Thread Pool의 단점을 개선한 클래스
- Task는 Background Thread이며 Thread Pool을 이용한다. - 예제 1
- Task의 생성에 람다식을 사용할 수 있다. - 예제 2
- Thread의
Join()
함수와 유사한Wait()
함수를 지원한다. - 예제 2, 3 ContinueWith()
함수를 통해 Task가 완료된 후에 실행할 Task를 지정할 수 있다. - 예제 4Run()
함수를 통해 Task의 생성과 시작을 한번에 실행할 수 있다. - 예제 5WhenAll()
함수를 통해 여러 Task들이 모두 완료될 때까지 대기한 후, 모두 완료되면 새로운 Task를 반환할 수 있다. - 예제 5WhenAny()
함수를 통해 여러 Task 중 가장 먼저 완료된 Task를 다루는 것도 가능하다. - 예제 6Run()
함수보다 더 많은 옵션과 기능을 제공하는Factory.StartNew()
도 존재한다. - 예제 7Task<>
로Func<>
처럼,Task
의 실행 결과로 값을 반환할 수 있다. - 예제 8
예제 1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class TestTask : MonoBehaviour
{
void Start()
{
Task task = new Task(BackgroundTask);
task.Start();
}
void BackgroundTask()
{
bool isBackground = Thread.CurrentThread.IsBackground;
Debug.Log($"백그라운드 스레드 : {isBackground}");
bool isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread;
Debug.Log($"스레드 풀 : {isThreadPoolThread}");
}
}
위 코드를 실행시켰을 때 결과는 모두 true로 출력된다.
즉, 현재 스레드는 Background Thread이고, Thread Pool에 속하는 스레드라는 것이다.
위 코드는 Task를 통해 실행된다.
이를 통해 Task는 Background Thread이며 Thread Pool을 이용한다는 것을 알 수 있다.
예제 2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestTask : MonoBehaviour
{
private void Start()
{
ThreadPool.QueueUserWorkItem((obj) =>
{
Debug.Log("ThreadPool 실행");
});
Task task = new Task(() =>
{
Debug.Log("Task 실행");
});
task.Start();
task.Wait();
}
}
Task의 생성자로 action을 받기 때문에, 위 코드와 같이 람다식을 활용할 수도 있겠다.
Wait()
함수로 Thread의 Join()
함수와 유사한 기능을 지원한다.
예제 3.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestTask : MonoBehaviour
{
private void Start()
{
Task task = new Task(SleepAction);
task.Start();
task.Wait(); // task가 완료될 때까지 현재 스레드 대기
Debug.Log("Task 완료");
}
void SleepAction()
{
Thread.Sleep(3000);
}
}
위 코드를 실행하면, 3초가 지난 후에 “Task 완료” 라는 문구가 콘솔창에 출력된다.
Wait()
함수는 이렇게 활용할 수 있겠다.
예제 4.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TestTask : MonoBehaviour
{
private void Start()
{
Task task = new Task(SleepAction);
// Task가 완료된 후 수행할 작업 지정
task.ContinueWith((obj) =>
{
Debug.Log("Task 완료");
});
task.Start();
}
void SleepAction()
{
Thread.Sleep(3000);
}
}
이는 예제 3과 겉으로는 동일한 기능을 하지만, ContinueWith()
함수를 사용한다.
ContinueWith()
함수는 Task가 완료된 후 실행할 Task를 지정한다.
기존의 Thread는 이러한 기능을 지원하지 않는다.
예제 5.
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
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class TestTask : MonoBehaviour
{
private Task task1, task2, task3;
private Stopwatch stopwatch;
private void Start()
{
stopwatch = new Stopwatch();
stopwatch.Start();
task1 = Task.Run(Task1Function);
task2 = Task.Run(() =>
{
Thread.Sleep(2000);
});
task3 = Task.WhenAll(task1, task2);
}
private void Update()
{
if (task3.IsCompleted)
{
stopwatch.Stop();
UnityEngine.Debug.Log("모든 작업이 완료되었습니다.");
UnityEngine.Debug.Log($"경과 시간 : {stopwatch.Elapsed.TotalSeconds}초");
}
}
void Task1Function()
{
Thread.Sleep(3000);
}
}
Run()
: Task의 생성과 시작을 한번에 하는 메서드WhenAll()
: Task가 모두 완료될 때까지 대기하고, 모두 완료되면 새로운 Task를 반환하는 메서드.
위 코드는 StopWatch를 활용해 task1, task2, task3가 모두 완료되는 데 걸리는 시간을 측정한다.
예제 6.
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
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
public class TestTask : MonoBehaviour
{
private Task task1, task2, task3;
private Stopwatch stopwatch;
private void Start()
{
stopwatch = new Stopwatch();
stopwatch.Start();
task1 = Task.Run(Task1Function);
task2 = Task.Run(() =>
{
Thread.Sleep(2000);
});
task3 = Task.WhenAny(task1, task2);
}
private void Update()
{
if (task3.IsCompleted)
{
stopwatch.Stop();
UnityEngine.Debug.Log("모든 작업이 완료되었습니다.");
UnityEngine.Debug.Log($"경과 시간 : {stopwatch.Elapsed.TotalSeconds}초");
}
}
void Task1Function()
{
Thread.Sleep(3000);
}
}
WhenAny()
: 매개변수 Task 중 먼저 완료된 Task를 반환한다.
task1보다 task2가 먼저 끝날 것이기 때문에, Task.WhenAny(task1, task2);
의 결과로 task2가 반환될 것이다.
예제 7.
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestTask : MonoBehaviour
{
private void Start()
{
Task task = Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
});
task.Wait();
Debug.Log("완료");
}
}
Task.Factory.StartNew()
:Run()
함수와 유사하게 새로운 작업(Task)을 생성하고 실행하는 데 사용되는 메서드다.Run()
함수보다 더 많은 제어와 옵션을 제공하지만, 그만큼 복잡할 수 있다.
예제 8.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestTask : MonoBehaviour
{
private void Start()
{
Task<int> task1 = new Task<int>(() => 1);
Task<float> task2 = Task.Run(() => 2.0f);
Task<long> task3 = Task.Factory.StartNew(() => 3L);
task1.Start();
task1.Wait();
task2.Wait();
task3.Wait();
Debug.Log(task1.Result);
Debug.Log(task2.Result);
Debug.Log(task3.Result);
}
}
위 코드의 실행결과는 1, 2, 3이 각각 콘솔창에 출력된다.
Func<>
처럼, Task
의 실행 결과로 반환값이 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestTask : MonoBehaviour
{
private void Start()
{
Task<int> task = Task.Run(Test);
task.Wait();
Debug.Log($"실행 결과 : {task.Result}");
}
int Test()
{
int result = 0;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(100);
result = i + 1;
Debug.Log($"반복 횟수 : {result}");
}
return result;
}
}
위 코드의 실행결과는 다음과 같다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.