[로봇활용_14주차] C# 프로세스와 스레드

최윤호·2025년 11월 6일
post-thumbnail

비동기 프로그래밍 첫걸음

C#을 배우다 보면 언젠가 마주치게 되는 거대한 산이 하나 있죠.
바로 비동기 프로그래밍(Asynchronous Programming)입니다.
우리가 작성한 코드는 결국 컴퓨터 위에서 돌아가게 되는데요,
이때 운영체제는 우리의 프로그램을 어떻게 실행하고 관리할까요?
이번 글에서는 비동기 처리의 바탕이 되는 두 가지 핵심 개념,
프로세스(Process)스레드(Thread)에 대해 이야기해 보려 합니다.

1)프로세스(Process)

프로세스는 쉽게 말해 '실행 중인 프로그램' 그 자체를 의미합니다.
여러분이 PC에서 Chrome브라우저를 켜고, Visual Studio를 실행했다면,
프로세스는 운영체제(Windows, Linux 등)에 의해 생성되고 관리되며,
자원(CPU, 메모리 등)을 할당하여 독립적으로 실행되는 작업 단위입니다.

프로세스는 '하나의 독립된 레스토랑'이라고 생각하면 쉽습니다.

  • 독립된 공간: 각 레스토랑(프로세스)은 자신만의 주방(메모리 공간)을 가지고 있어요.
  • 자원 소유: 레스토랑이 운영되려면 가스, 전기, 수도 같은 자원이 필요하듯,
    프로세스도 실행되기 위해 메모리, CPU 같은 자원을 운영체제로부터 할당받습니다.
  • 안정성: 옆집 레스토랑 주방에 불이 나도 우리 레스토랑은 안전한 것처럼,
    하나의 프로세스에 오류가 발생해도 다른 프로세스에 직접적인 영향을 주지 않습니다.

2)C#에서 프로세스 조회

C#에서는 System.Diagnostics네임스페이스의 Process클래스를 통해
현재 실행 중인 프로세스 정보에 접근할 수 있습니다.

[코드]

using System;
using System.Diagnostics;

Process currentProcess = Process.GetCurrentProcess();

Console.WriteLine($"현재 프로세스 이름: {currentProcess.ProcessName}");
Console.WriteLine($"현재 프로세스 ID: {currentProcess.Id}");
Console.WriteLine($"사용 중인 메모리: {currentProcess.WorkingSet64 / 1024 / 1024} MB");

[실행 결과]

현재 프로세스 이름: 실행 중인 C# 프로그램
현재 프로세스 ID: 15520
사용 중인 메모리: 21 MB

이렇게 각 프로세스는 자신만의 고유한 ID와
독립된 메모리 영역을 가지고 운영체제로부터 관리받습니다.

3)스레드(Thread)

스레드는 프로세스 안에서 실제로 작업을 수행하는 '일꾼'입니다.
하나의 프로세스 안에는 최소 하나 이상의 '스레드'가 존재합니다.

스레드는 레스토랑(프로세스) 안에서 일하는 '요리사'와 같아요.

  • 작업 수행: 요리사가 재료를 다듬고, 불을 사용해 요리를 만드는 것처럼,
    스레드가 CPU를 사용해서 코드의 명령어를 하나씩 실행합니다.
  • 자원 공유: 레스토랑에 있는 여러 명의 요리사들이 같은 주방을 '공유'해서 사용하죠?
    하나의 프로세스에 속한 여러 개의 스레드는 그 프로세스의 자원을 공유합니다.
  • 적은 비용: 요리사를 고용하는 것은 레스토랑을 새로 짓는 것보다 빠르고 저렴합니다.

싱글스레드 vs 멀티스레드

  • 싱글스레드(Single-thread): 주방에 '요리사'가 한 명뿐인 경우입니다.
    파스타를 삶는 동안(시간이 걸리는 작업) 다른 일(채소 다지기)을 할 수 없습니다.
    프로그램이 한 번에 하나의 작업만 순서대로 처리하는 방식이죠.
  • 멀티스레드(Multi-thread): 주방에 '요리사'가 여러 명인 경우입니다.
    한 명은 파스타를 삶고, 다른 한 명은 샐러드 채소를 다듬을 수 있습니다.
    여러 작업을 동시에 처리해서 프로그램의 전체적인 효율을 높이는 방식입니다.

멀티태스킹의 원리

우리가 사용하는 CPU는 한 번에 하나의 일만 처리할 수 있습니다.
그렇다면 여러 개의 스레드가 동시에 돌아가는 것처럼 보이는 이유는 뭘까요?

컨텍스트 스위칭(Context Switching)
운영체제는 매우 짧은 시간 간격으로 이 스레드, 저 스레드를 번갈아 가며 실행합니다.
그래서 여러 작업이 동시에 실행되는 것처럼 보이게 하는 멀티태스킹이 가능해집니다.
이 과정은 약간의 자원이 소요됩니다. 이것을 오버헤드(Overhead)라고 합니다.
따라서 너무 많은 스레드는 프로그램 성능에 좋지 않은 영향을 줄 수 있습니다.

독립적인 실행 보장

각 스레드는 독립적인 실행을 위해 스택(Stack), 프로그램 카운터(PC), 레지스터를 갖습니다.

  • 스택(Stack): 아무리 주방을 공유하더라도, 각 요리사는 개인 작업대가 필요합니다.
    만약 모든 요리사가 하나의 작업대를 쓴다면, 재료와 도구가 뒤섞어 엉망이 될 겁니다.
    그래서 자신만의 작업대를 가져야만 안전하게 일을 처리할 수 있습니다.
  • 프로그램 카운터(Program Counter): 요리사가 보고 있는 레시피의 페이지 번호입니다.
    파스타 요리사(스레드 A)는 레시피의 5번 페이지를 보고 있고,
    스테이크 요리사(스레드 B)는 레시피의 32번 페이지를 보고 있을 수 있죠.
    각 스레드는 '다음에 실행할 명령어의 주소'를 프로그램 카운터에 저장합니다.
  • 레지스터(Registers): 레지스터를 비유하자면 '요리사의 손'과 같습니다.
    파스타 요리사(스레드 A)가 손에 마늘 한 줌을 쥐고 있고,
    스테이크 요리사(스레드 B)는 소금 통을 쥐고 고기에 뿌리려고 합니다.
    레지스터는 CPU 내부에 있는 '아주 작은 초고속 임시 저장 장치'입니다.

4)스레드 직접 만들어보기

자, 이제 C# 코드로 우리 레스토랑의 요리사들을 직접 만들어 볼까요?
System.Threading네임스페이스의 Thread클래스를 사용하면 직접 만들 수 있습니다.

[코드]

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // '파스타 요리사' 스레드 생성
        Thread pastaChef = new Thread(CookPasta);
        // '스테이크 요리사' 스레드 생성
        Thread steakChef = new Thread(CookSteak);

        Console.WriteLine("레스토랑 오픈! 요리를 시작합니다.");

        // 요리사 일 시작!
        pastaChef.Start();
        steakChef.Start();

        // 메인 스레드(매니저)는 자신의 일을 합니다.
        for (int i = 0; i <= 5; i++)
        {
            Thread.Sleep(100);
            Console.WriteLine($"매니저: 일하는 중... {i}");
        }
        Console.WriteLine("매니저: 작업 완료!");

        // 메인 스레드(매니저)는 요리사들이 일을 마칠 때까지 기다립니다.
        pastaChef.Join();
        steakChef.Join();

        Console.WriteLine("매니저: 모든 요리가 완료되었습니다!");
    }

    // 파스타를 만드는 작업
    static void CookPasta()
    {
        Console.WriteLine("파스타 요리사: 면을 삶기 시작합니다.");
        Thread.Sleep(3000); // 3초 동안 면을 삶는 중
        Console.WriteLine("파스타 요리사: 소스를 만들고 있습니다.");
        Thread.Sleep(2000); // 2초 동안 소스 만드는 중
        Console.WriteLine("파스타 요리사: 파스타 완성!");
    }

    // 스테이크를 굽는 작업
    static void CookSteak()
    {
        Console.WriteLine("스테이크 요리사: 고기를 손질합니다.");
        Thread.Sleep(1000); // 1초 동안 고기 손질 중
        Console.WriteLine("스테이크 요리사: 스테이크를 굽습니다.");
        Thread.Sleep(4000); // 4초 동안 굽는 중
        Console.WriteLine("스테이크 요리사: 스테이크 완성!");
    }
}

[실행 결과]

레스토랑 오픈! 요리를 시작합니다.
파스타 요리사: 면을 삶기 시작합니다.
스테이크 요리사: 고기를 손질합니다.
매니저: 일하는 중... 0
매니저: 일하는 중... 1
매니저: 일하는 중... 2
매니저: 일하는 중... 3
매니저: 일하는 중... 4
매니저: 일하는 중... 5
매니저: 작업 완료!
스테이크 요리사: 스테이크를 굽습니다.
파스타 요리사: 소스를 만들고 있습니다.
파스타 요리사: 파스타 완성!
스테이크 요리사: 스테이크 완성!
매니저: 모든 요리가 완료되었습니다!

위 코드를 실행해 보면, 요리가 동시에 진행되는 것을 볼 수 있을 거예요.
여기서 코드를 실행할 때마다 출력 순서가 조금씩 바뀔 수도 있는데,
그 이유는 스레드에 대한 결정권이 우리가 아닌 운영체제에 있기 때문이죠.
스레드들의 실행 순서는 운영체제의 '스레드 스케줄링' 정책에 따라 달라집니다.
여러 스레드를 동시에 실행할 때, 프로그래머는 실행 순서를 가정해서는 안 됩니다.

5)정리

앞으로 배울 비동기 프로그래밍은 '스레드'를 효율적으로 다루기 위한 고수준의 기술입니다.
'스레드'에 대한 기본 원리를 아는 것은 비동기 프로그래밍에서 큰 도움이 됩니다.

구분프로세스(Process)스레드(Thread)
비유독립된 레스토랑레스토랑 안의 요리사
정의실행 중인 프로그램의 인스턴스프로세스 내의 실행 흐름 단위
메모리독립적인 메모리 공간 사용같은 프로세스 내의 메모리 공유
생성 비용크다 (운영체제 차원의 자원 할당)작다 (프로세스 내에서 생성)
통신비교적 복잡하며 비용이 큼 (IPC 필요)비용이 적고 효율적 (메모리 공유)
안정성하나가 죽어도 다른 프로세스는 안전스레드 하나가 오류를 내면 프로세스가 종료될 수 있음
profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글