Study/C#

ThreadPool 사용 예시

13.d_dk 2021. 5. 28. 17:40
728x90
반응형

ThreadPool은 무엇일까? 또 어디에 사용하면 좋을까?

 여러 thread를 사용하며 프로그램을 동작시키는 경우 리소스 관리로 인한 문제가 발생할 수 있다. 여기서 리턴 값을 받지 않아도 되는 동작이 있다면 ThreadPool이라는 것을 사용하며 효율적으로 리소스를 관리할 수 있다. ThreadPool은 기존에 있는 thread에 할당하여 사용하는 방법이다. ThreadPool은 새로운 thread가 아닌 기존에 존재하는 thread를 재사용하여 효율적으로 리소스를 관리할 수 있다. 리턴 값을 받을 필요가 없는 예시로는 소프트웨어 동작 로그가 있다. 

 

문제의 정의

 하나의 객체에서 어떤 값을 파일로 저장한다. 이때 여러 thread에서 이 객체의 파일 저장을 수행할 수 있다. A라는 동작 이후 저장할 수도 있고 B라는 동작 이후 저장할 수도 있다. 이때 A 동작 후 파일 쓰기를 수행 중인데 B 동작으로 인한 파일 쓰기가 실행될 때 예외가 발생한다. 이를 ThreadPool을 사용하면 해결할 수 있다.

동작 A, B에 따른 쓰기 동작에서 예외 발생 도식

 

문제가 발생되는 코드

 WriteLog라는 클래스를 통해 하나의 객체를 생성하였다. 먼저 객체에 파일 이름과 데이터를 저장한다. 하나의 thread에 이 객체의 파일 저장 동작을 할당하였다.(위의 예시 중 동작 A에 해당)

 이후 다른 파일 이름과 데이터를 객체에 저장한다. 이 값을 파일로 출력하기 위해 파일 저장 동작을 수행하였다. (위의 예시 중 동작 B에 해당) 

 코드는 아래와 같으며 2번째 파일 쓰기 중 예외가 발생하는 것을 확인할 수 있다.

 

  • WriteLog.cs
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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
 
namespace threadPoolTest
{
    public class WriteLog
    {
        public string WritePath = "";
        public List<string> LogStrList = null;
 
        public WriteLog()
        {
            LogStrList = new List<string>();
        }
 
        public void Write()
        {
            if (WritePath == "" || LogStrList == null)
            {
                return;
            }
 
            using (StreamWriter writer = new StreamWriter(WritePath))
            {
                foreach (var line in LogStrList)
                {
                    writer.WriteLine(line);
                }
 
                LogStrList = new List<string>();
            }
        }
    }
}
 
cs

 

  • Program.cs
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
 
namespace threadPoolTest
{
    class Program
    {
        static WriteLog writeLog = null;
 
        static void Main(string[] args)
        {
            writeLog = new WriteLog();
            
            writeLog.WritePath = "test1.txt";
            SetLogData1();
 
            Thread thread = new Thread(writeLog.Write);
            thread.Start(); // writeLog.Write();
 
            writeLog.WritePath = "test2.txt";
            SetLogData2();
            writeLog.Write();
        }
 
        static void SetLogData1()
        {
            if (writeLog == null)
            {
                return;
            }
 
            int numOfRand = 1000000;
            Random rand = new Random();
            int randNum = 0;
 
            for (int i = 0; i < numOfRand; i++)
            {
                randNum = rand.Next(numOfRand);
                writeLog.LogStrList.Add(String.Format("randNum[idx] : {0}[{1}]", randNum, i));
            }
        }
 
        static void SetLogData2()
        {
            if (writeLog == null)
            {
                return;
            }
 
            int numOfRand = 10;
            Random rand = new Random();
            int randNum = 0;
 
            for (int i = 0; i < numOfRand; i++)
            {
                randNum = rand.Next(numOfRand);
                writeLog.LogStrList.Add(String.Format("randNum[idx] : {0}[{1}]", randNum, i));
            }
        }
    }
}
 
cs

 

 

문제를 해결하기 위한 ThreadPool 코드

 이를 해결하기 위해 ThreadPool을 사용하자. ThreadPool은 System.Threading 네임스페이스에서 사용할 수 있다. WriteLog 클래스에서 쓰기 동작에 해당하는 Write 함수를 변경한다. private로 wrtie 함수를 만들어 원래의 Write 함수에는 ThreadPool.QueueUserWorkItem(write)를 작성한다. ThreadPool에 Queue로 동작들을 할당하여 쓰기 동작을 순차적으로 하나의 thread에서 처리할 수 있게 해주는 방식이다. 코드는 아래와 같다.

 

  • WriteLog.cs
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
41
42
43
44
45
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
 
namespace threadPoolTest
{
    public class WriteLog
    {
        public string WritePath = "";
        public List<string> LogStrList = null;
 
        public WriteLog()
        {
            LogStrList = new List<string>();
        }
 
        public void Write()
        {
            ThreadPool.QueueUserWorkItem(write);
        }
 
        private void write(object obj)
        {
            if (WritePath == "" || LogStrList == null)
            {
                return;
            }
 
            using (StreamWriter writer = new StreamWriter(WritePath))
            {
                foreach (var line in LogStrList)
                {
                    writer.WriteLine(line);
                }
 
                LogStrList = new List<string>();
            }
        }
    }
}
 
cs

ThreadPool 동작 설명 도식

 

Reference

 

반응형