Thread Pool

Source code

https://github.com/teamsmiley/gitbook-sample/tree/main/ThreadTest

Thread

Thread๋Š” ํ•˜๋‚˜์˜ ํ”„๋กœ์„ธ์Šค์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์—…์˜ ๋‹จ์œ„์ด๋‹ค. Thread๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. Thread๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Thread๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ๋น„์šฉ์ด ๋งŽ์ด ๋“ ๋‹ค. ๊ทธ๋ž˜์„œ .NET์—์„œ๋Š” Thread Pool์„ ์ œ๊ณตํ•œ๋‹ค.

Thread Pool

Thread Pool์€ Thread๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. Thread Pool์„ ์‚ฌ์šฉํ•˜๋ฉด Thread๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋น„์šฉ์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. Thread Pool์€ Thread๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. Thread Pool์„ ์‚ฌ์šฉํ•˜๋ฉด Thread๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— Thread๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋ณด๋‹ค ๋” ์ ์€ ๋น„์šฉ์œผ๋กœ Thread๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

webapi

ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŸ์ง€๋งŒ dotnet์œผ๋กœ webapi๋ฅผ ์‚ฌ์šฉ์‹œ kestrel์ด๋ผ๋Š” ์›น์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. kestrel์€ ๊ธฐ๋ณธ์ ์œผ๋กœ thread pool์„ ์‚ฌ์šฉํ•œ๋‹ค. ํ™•์ธํ•ด๋ณด์ž.

cd ~/Desktop
dotnet new webapi --name ThreadTest

cd ThreadTest

vi controllers/ValuesController.cs
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace ThreadTest.Controllers;

[ApiController]
[Route("values")]
public class ValuesController : ControllerBase
{
    private readonly ILogger<ValuesController> _logger;
    public ValuesController(ILogger<ValuesController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetValues")]
    public int Get()
    {
        Console.WriteLine($"The number of processors on this computer is {Environment.ProcessorCount}.");

        var maxWorkerThreads = 0;
        var maxCompletionPortThreads = 0;

        ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
        Console.WriteLine("Maximum worker threads: {0}", maxWorkerThreads);

        var availableWorkerThreads = 0;
        var completionPortThreads = 0;

        ThreadPool.GetAvailableThreads(out availableWorkerThreads, out completionPortThreads);
        Console.WriteLine($"Available Worker threads: {availableWorkerThreads}", availableWorkerThreads);

        var usedWorkerThread = maxWorkerThreads - availableWorkerThreads;
        Console.WriteLine($"Used worker threads: {usedWorkerThread}");

        Thread.Sleep(10000); // Sleep for 10 seconds

        return usedWorkerThread;
    }
}

์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹คํ–‰ํ•ด๋ณด์ž.

dotnet run

curl http://localhost:5007/values
  • ํ˜„์žฌ ์ปดํ“จํ„ฐ๋Š” 6์ฝ”์–ด 12์Šค๋ ˆ๋“œ์ด๋‹ค. 12๊ฐœ์˜ cpu๋ฅผ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

  • ์ตœ๋Œ€ worker thread๋Š” 32767๊ฐœ์ด๋‹ค.

  • ์‚ฌ์šฉ๋œ worker thread๋Š” 2๊ฐœ์ด๋‹ค. (1๋ฒˆ์˜ ํ˜ธ์ถœ์„ ํ…Œ์ŠคํŠธํ•ด๋ณด์•˜๋‹ค.) 1๊ฐœ๋Š” ๊ธฐ๋ณธ์“ฐ๋ ˆ๋“œ์ธ๊ฐ€? ํ˜ธ์ถœ ํ•œ๋ฒˆ์— 2๋ฒˆ์งธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ์ด ๋˜์—ˆ๋‹ค.

  • ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ worker thread๋Š” 32765๊ฐœ์ด๋‹ค.

The number of processors on this computer is 12.
Maximum worker threads: 32767
Available Worker threads: 32765
Used worker threads: 2

ํ…Œ์ŠคํŠธ๋ฅผ ๊ณ„์†ํ•ด๋ณด๋ฉด usedWorkerThread๊ฐ€ ๊ณ„์† ๋Š˜์–ด๋‚˜๋Š”๊ฒƒ์„ ์•Œ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹œ๊ฐ„์ด ๋˜๊ณ  ๋‚˜๋ฉด usedWorkerThread๊ฐ€ ์ค„์–ด๋“œ๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

alt text

์“ฐ๋ ˆ๋“œ๊ฐ€ ์ฆ๊ฐ€ํ–‡๋‹ค๊ฐ€ ๋ฆฌํ„ด๋˜๋Š”๊ฒƒ์„ ๋ณผ์ˆ˜ ์žˆ๋‹ค.

32000๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๊ฐ€ ํ’€์—๋Š” ๋งŒ๋“ค์–ด์ ธ์žˆ๋Š”๊ฑฐ๊ฐ™๋‹ค. ๊ทธ๋Ÿฌ๋ฉด 32000๊ฐœ๋ฅผ ๋ฏธ๋ฆฌ ๋‹ค ๋งŒ๋“ค์–ด๋‘๋Š”๊ฒƒ์ธ๊ฐ€? ์•„๋‹ˆ๋ฉด ํ•„์š”ํ• ๋•Œ๋งˆ๋‹ค ๋งŒ๋“œ๋Š”๊ฒƒ์ธ๊ฐ€? ์ด๊ฒƒ์„ ํ™•์ธํ•ด๋ณด์ž.

dotnet-counters

dotnet tool install --global dotnet-counters

dotnet-counters monitor -n ThreadTest # ํ”„๋กœ์„ธ์Šค ์ด๋ฆ„
alt text

์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์˜ฌ๋ผ๊ฐ€๋Š” ์“ฐ๋ ˆ๋“œ๋ฅผ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

alt text

์š”์ฒญ์„ ๊ณ„์† ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ que์— ๋“ค์–ด๊ฐ€๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

locust.io

https://locust.io/

๋งŽ์€ ์š”์ฒญ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ด๊ฑธ ์‚ฌ์šฉํ•œ๋‹ค.

pip install locust
mkdir locust
cd locust

cat > locust.py <<EOF
import time
from locust import HttpUser, between, task

class WebsiteUser(HttpUser):
  wait_time = between(1, 2)

  def on_start(self):
    self.client.verify = False

  @task
  def launch(self):
      self.client.get(url="/values")
EOF
locust -f locust.py  --host http://localhost:5007 --users 1000 --spawn-rate 200

http://localhost:8089

์‹คํ–‰์„ ํ•ด๋ณด๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ณผ์ˆ˜ ์žˆ๋‹ค.

alt text
alt text

๊ฒฐ๊ณผ๊ฐ€ ์ž˜ ํ™•์ธ๋จ์„ ์•Œ์ˆ˜ ์žˆ๋‹ค.

container

์‹ค์ œ ์„œ๋น„์Šคํ™˜๊ฒฝ์—์„œ๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ด์ œ ์ง€๊ธˆ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋žจ์„ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋งŒ๋“ค๊ณ  ํ™œ์šฉํ•ด๋ณด์ž.

vi Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build-env
WORKDIR /app
COPY . .
RUN dotnet publish "ThreadTest.csproj" -c Release -o /app/publish

# Install dotnet debug tools
RUN dotnet tool install --tool-path /tools dotnet-trace \
  && dotnet tool install --tool-path /tools dotnet-counters \
  && dotnet tool install --tool-path /tools dotnet-dump \
  && dotnet tool install --tool-path /tools dotnet-gcdump

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine

# Copy dotnet-tools
WORKDIR /tools
COPY --from=build-env /tools .

RUN apk add icu-libs

ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

WORKDIR /app

COPY --from=build-env /app/publish .
EXPOSE 80
ENTRYPOINT ["dotnet", "ThreadTest.dll"]
vi docker-compose.yaml
version: '3'
services:
  web:
    build:
      context: .
    ports:
      - 80:80
    deploy:
      resources:
        limits:
          cpus: '1'
        reservations:
          cpus: '1'

cpu๋ฅผ 1๊ฐœ๋งŒ ํ• ๋‹น์„ ํ•˜์˜€๋‹ค. ์ด์ œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž.

docker-compose up --build
docker ps -a

์ด์ œ locust๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž. ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ๋ฅผ ๋ด๋ณด์ž.

docker logs -f threadtest-web-1

๊ทธ๋ฆฌ๊ณ  ๋ชจ๋‹ˆํ„ฐ๋ง๋„ ํ•ด๋ณด์ž.

docker exec -it threadtest-web-1 /tools/dotnet-counters monitor --process-id 1
alt text

์ž˜ ๋ณด์ธ๋‹ค.

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

cpu๋ฅผ 1๊ฐœ๋งŒ ํ• ๋‹นํ•˜๊ณ  ๊ทธ๋ฆฌ๊ณ  12๊ฐœ๋ฅผ ํ• ๋‹นํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•ด๋ณด๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

cpu
maxWorkerThread
usedWorkerThread
AvailableWorkerThread

1

32767

1

32766

12

32767

2

32765

cpu ๊ฐฏ์ˆ˜์™€ ์ƒ๊ด€์ด ์—†์ด Thread๋Š” 32767๊ฐœ๋กœ ๊ณ ์ •๋˜์–ด์žˆ๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

์“ฐ๋ ˆ๋“œ์— ๊ธฐ๋ณธ ๊ฐฏ์ˆ˜๊ฐ€ ์žˆ๊ณ  ๊ทธ๊ฒŒ ๋„˜์–ด๊ฐ€๋ฉด ์ž๋™์œผ๋กœ ์ƒ์„ฑ์„ ํ•˜๋Š”๊ฒƒ์œผ๋กœ ์•Œ๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿผ ๊ธฐ๋ณธ๊ฐ’์ด ๋‹ค๋ฅธ๊ฒƒ์ผ๊ฐ€?

์ฝ”๋“œ์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹คํ–‰ํ•ด๋ณด์ž.

var usedWorkerThread = maxWorkerThreads - availableWorkerThreads;
Console.WriteLine($"Used worker threads: {usedWorkerThread}");

# ์ด๊ฑฐ ์ถ”๊ฐ€
int minWorker, minIOC;
// Get the current settings.
ThreadPool.GetMinThreads(out minWorker, out minIOC);
Console.WriteLine("Minimum worker threads: {0}", minWorker);

Thread.Sleep(1000000); // Sleep for 10 seconds
Console.WriteLine("Thread completed. go back to the pool.");
return usedWorkerThread;

minWorker๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

cpu
minWorker

1

1

12

12

minWorker๋Š” cpu ๊ฐฏ์ˆ˜์™€ ๊ฐ™์€๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

cpu๋ฅผ 1๊ฐœ๋กœ ํ•˜๊ณ  minworker๋ฅผ 1000๊ฐœ๋กœ ํ•ด๋ณผ๊ฐ€?

vi ThreadTest.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ThreadPoolMinThreads>1000</ThreadPoolMinThreads> <!-- ์ด๊ฑฐ ์ถ”๊ฐ€ -->
  </PropertyGroup>
</Project>

๋‹ค์‹œ ์‹คํ–‰ํ•ด๋ณด์ž.

alt text

๊ธฐ๋ณธ์ ์œผ๋กœ 1000๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ usedWorkerThread๊ฐ€ 1000๊ฐœ๋กœ ๋‚˜์˜จ๋‹ค.

๋ฆฌํ€˜์ŠคํŠธ๊ฐ€ ์ƒ๊น€์œผ๋กœ์จ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋Š˜์–ด๋‚˜๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์—†๋‹ค. 1000์ด ๋„˜์œผ๋ฉด ์ฆ๊ฐ€๋ฅผ ํ• ๊ฑฐ ๊ฐ™๋‹ค.

alt text

1000์ด ๋„˜์–ด๊ฐ€๋ฉด ํ์— ๋“ค์–ด๊ฐ€๊ณ  ํ•˜๋‚˜์”ฉ ์ƒ์„ฑ์ด ๋จ์„ ์•Œ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์‹œ ๊ณ ๋ฏผ

์นœ๊ตฌ์˜ ๋ฌธ์ œ๋Š” ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ƒ๊ธฐ๋Š”๊ฒŒ ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค๋Š”๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์“ฐ๋ ˆ๋“œ๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘๋Š”๊ฒƒ์ด ์ข‹์„๊ฑฐ ๊ฐ™๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์•„๋ฌด๋ฆฌ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ด๋„ ๊ฐ„๋‹จํ•œ ์ž‘์—…์€ Thread๊ฐ€ ๋งŒ๋“ค์–ด์ง€๋Š”๊ฒƒ์ด ์ฒ˜์Œ 10๊ฐœ์™€ 1000๊ฐœ์˜ ์ฐจ์ด๊ฐ€ ์—†๋‹ค. ์•„๋ฌด๋ž˜๋„ ์“ฐ๋ ˆ๋“œ์˜ ์ƒ์„ฑ์ด ๋ฌธ์ œ๋Š” ์•„๋‹Œ๊ฑฐ ๊ฐ™๋‹ค. ๊ทธ๋Ÿผ ๋‹ค๋ฅธ๊ฒƒ์„ ์ฐพ์•„๋ด์•ผ๊ฒ ๋‹ค.

entity framework

entity framework๋ฅผ ์‚ฌ์šฉ์‹œ์— ์ปค๋„ฅ์…˜ pool์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š”๊ฑด ์•„๋‹๊ฐ€?

๋‹ค์Œ ๊ธ€์—์„œ ํ™•์ธํ•ด๋ณด์ž.

Last updated

Was this helpful?