[파이썬] 동시성 구현을 위한 예제 - Thread / Process / Coroutine 기반 스피너 예제 (260811)

WonTerry·2026년 8월 11일

Python

목록 보기
25/28

Python 동시성 간단 설명

Python에서 동시성은 하나의 프로그램이 여러 작업을 동시에 또는 거의 동시에 처리하는 능력입니다.

동시성의 목적

  • 성능 향상: 여러 작업을 동시에 처리하여 전체 실행 시간 단축
  • 응답성 향상: 사용자 인터페이스가 멈추지 않고 작업을 처리
  • 리소스 효율성: 하나의 프로세스에서 여러 작업을 관리

Python에서의 세 가지 주요 동시성 방식:

1. Thread (스레드)

  • 하나의 프로그램 내에서 여러 작업을 병렬로 실행
  • I/O 바운드 작업(네트워크, 파일 읽기/쓰기)에 적합
  • GIL(Global Interpreter Lock)로 인해 CPU 바운드 작업에는 제한적

2. Process (프로세스)

  • 각각의 독립적인 메모리 공간에서 작업 실행
  • CPU 바운드 작업(계산이 많은 작업)에 적합
  • 실제 병렬 처리 가능 (GIL 문제 없음)

3. Coroutine (코루틴)

  • 하나의 스레드에서 여러 작업을 교대로 실행
  • I/O 바운드 작업에 매우 효율적
  • 메모리 사용량이 적고 높은 성능

간단한 예시

# Thread 방식 - 여러 작업이 동시에 실행됨
import threading

def work(name):
    print(f"{name} 작업 시작")
    # 작업 수행
    print(f"{name} 작업 완료")

# 두 개의 작업을 동시에 실행
t1 = threading.Thread(target=work, args=("작업1",))
t2 = threading.Thread(target=work, args=("작업2",))

t1.start()
t2.start()

요약: Python 동시성은 프로그램이 여러 작업을 효과적으로 관리하고 처리하는 기술입니다. 작업의 성격에 따라 적절한 방식을 선택해야 합니다.


Python 동시성 예제

1. Thread 기반 스피너 예제

# 필요한 모듈을 불러옵니다
import threading    # 스레드를 사용하기 위한 모듈
import time         # 시간을 다루기 위한 모듈
import sys          # 시스템 관련 작업을 위한 모듈

# 스피너를 실행하는 함수 (스레드에서 실행됨)
def spinner_thread(name, delay):
    '''스레드에서 실행되는 스피너 함수'''
    # 다양한 스피너 모양을 정의합니다
    spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
    idx = 0  # 스피너 모양의 인덱스를 초기화
    
    # 무한 루프로 스피너를 계속 실행
    while True:
        # 현재 스피너 모양을 출력 (줄 바꿈 없이)
        sys.stdout.write(f'\r{name}: {spinner_chars[idx]}')
        # 출력 내용을 즉시 보여줍니다
        sys.stdout.flush()
        # delay 시간만큼 기다립니다
        time.sleep(delay)
        # 다음 스피너 모양으로 이동 (리스트의 끝에 도달하면 처음으로 돌아감)
        idx = (idx + 1) % len(spinner_chars)

# 메인 함수 - 프로그램의 시작점
def main():
    # 첫 번째 스피너 스레드를 생성 (Thread-1, 0.2초 간격)
    t1 = threading.Thread(target=spinner_thread, args=('Thread-1', 0.2))
    # 두 번째 스피너 스레드를 생성 (Thread-2, 0.15초 간격)
    t2 = threading.Thread(target=spinner_thread, args=('Thread-2', 0.15))
    
    # 스레드를 데몬으로 설정 (메인 프로그램이 종료되면 자동으로 종료됨)
    t1.daemon = True
    t2.daemon = True
    
    # 두 스레드를 실행 시작
    t1.start()
    t2.start()
    
    # 5초 동안 작업을 시뮬레이션
    time.sleep(5)
    
    # 작업 완료 메시지를 출력
    print('\nThread 기반 스피너 예제 완료!')

# 프로그램이 직접 실행될 때만 아래 코드를 실행
if __name__ == '__main__':
    main()

주요 특성:

  • 파이썬의 threading 모듈을 사용하여 동시 실행
  • 스레드는 동일한 메모리 공간을 공유 (GIL 제약)
  • I/O 바운드 작업에 적합
  • 구현이 간단하지만 파이썬의 GIL로 인해 제한됨

2. Process 기반 스피너 예제

# 필요한 모듈을 불러옵니다
import multiprocessing    # 프로세스를 사용하기 위한 모듈
import time               # 시간을 다루기 위한 모듈
import sys                # 시스템 관련 작업을 위한 모듈

# 스피너를 실행하는 함수 (프로세스에서 실행됨)
def spinner_process(name, delay):
    '''프로세스에서 실행되는 스피너 함수'''
    # 다양한 스피너 모양을 정의합니다
    spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
    idx = 0  # 스피너 모양의 인덱스를 초기화
    
    # 무한 루프로 스피너를 계속 실행
    while True:
        # 현재 스피너 모양을 출력 (줄 바꿈 없이)
        sys.stdout.write(f'\r{name}: {spinner_chars[idx]}')
        # 출력 내용을 즉시 보여줍니다
        sys.stdout.flush()
        # delay 시간만큼 기다립니다
        time.sleep(delay)
        # 다음 스피너 모양으로 이동 (리스트의 끝에 도달하면 처음으로 돌아감)
        idx = (idx + 1) % len(spinner_chars)

# 메인 함수 - 프로그램의 시작점
def main():
    # 첫 번째 스피너 프로세스를 생성 (Process-1, 0.2초 간격)
    p1 = multiprocessing.Process(target=spinner_process, args=('Process-1', 0.2))
    # 두 번째 스피너 프로세스를 생성 (Process-2, 0.15초 간격)
    p2 = multiprocessing.Process(target=spinner_process, args=('Process-2', 0.15))
    
    # 두 프로세스를 실행 시작
    p1.start()
    p2.start()
    
    # 5초 동안 작업을 시뮬레이션
    time.sleep(5)
    
    # 프로세스 종료 (실제 애플리케이션에서는 더 잘 처리해야 함)
    p1.terminate()
    p2.terminate()
    
    # 작업 완료 메시지를 출력
    print('\nProcess 기반 스피너 예제 완료!')

# 프로그램이 직접 실행될 때만 아래 코드를 실행
if __name__ == '__main__':
    main()

주요 특성:

  • 파이썬의 multiprocessing 모듈을 사용하여 진정한 병렬 처리
  • 각 프로세스는 자신만의 메모리 공간을 가짐
  • GIL 제약을 우회함
  • CPU 바운드 작업에 더 적합
  • 스레드보다 더 많은 메모리 사용

3. Coroutine 기반 스피너 예제

# 필요한 모듈을 불러옵니다
import asyncio      # 비동기 프로그래밍을 위한 모듈
import sys          # 시스템 관련 작업을 위한 모듈

# 스피너를 실행하는 함수 (코루틴에서 실행됨)
async def spinner_coroutine(name, delay):
    '''코루틴으로 실행되는 스피너 함수'''
    # 다양한 스피너 모양을 정의합니다
    spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
    idx = 0  # 스피너 모양의 인덱스를 초기화
    
    # 무한 루프로 스피너를 계속 실행
    while True:
        # 현재 스피너 모양을 출력 (줄 바꿈 없이)
        sys.stdout.write(f'\r{name}: {spinner_chars[idx]}')
        # 출력 내용을 즉시 보여줍니다
        sys.stdout.flush()
        # delay 시간만큼 기다립니다 (비동기적으로 기다림)
        await asyncio.sleep(delay)
        # 다음 스피너 모양으로 이동 (리스트의 끝에 도달하면 처음으로 돌아감)
        idx = (idx + 1) % len(spinner_chars)

# 메인 함수 - 프로그램의 시작점
async def main():
    # 첫 번째 스피너 코루틴을 생성 (Coroutine-1, 0.2초 간격)
    task1 = asyncio.create_task(spinner_coroutine('Coroutine-1', 0.2))
    # 두 번째 스피너 코루틴을 생성 (Coroutine-2, 0.15초 간격)
    task2 = asyncio.create_task(spinner_coroutine('Coroutine-2', 0.15))
    
    # 5초 동안 작업을 시뮬레이션
    await asyncio.sleep(5)
    
    # 태스크 취소 (실제 애플리케이션에서는 더 잘 처리해야 함)
    task1.cancel()
    task2.cancel()
    
    # 작업 완료 메시지를 출력
    print('\nCoroutine 기반 스피너 예제 완료!')

# 프로그램이 직접 실행될 때만 아래 코드를 실행
if __name__ == '__main__':
    # asyncio 이벤트 루프를 실행하여 비동기 작업을 처리
    asyncio.run(main())

주요 특성:

  • 파이썬의 asyncio를 사용한 비동기 프로그래밍
  • 단일 스레드, 협력적 멀티태스킹
  • I/O 바운드 작업(네트워크 요청, 파일 조작)에 매우 효율적
  • 스레드나 프로세스보다 메모리 효율적
  • async/await 문법과 이벤트 루프 관리 필요

세 가지 접근 방식의 차이점:

Thread (스레드):

  • 여러 작업을 동시에 실행
  • 동일한 메모리 공간 공유 (GIL로 인해 일부 제약)
  • 네트워크 요청이나 파일 읽기 같은 작업에 적합

Process (프로세스):

  • 각각의 독립적인 메모리 공간에서 작업 실행
  • 실제 병렬 처리 (GIL 문제 없음)
  • CPU 계산이 많은 작업에 적합

Coroutine (코루틴):

  • 하나의 스레드에서 여러 작업을 교대로 실행
  • 메모리 사용량이 적고 효율적
  • 네트워크 요청이나 파일 작업 같은 I/O 작업에 매우 효과적

각 접근 방식의 장점:

  • 스레드는 I/O 바운드 작업과 공유 상태에 적합
  • 프로세스는 진정한 병렬 처리를 제공하지만 더 많은 리소스 사용
  • 코루틴은 I/O 바운드 작업에 매우 효율적이며 최소한의 오버헤드로 작업 수행
profile
Hello, I'm Terry! 👋 Enjoy every moment of your life! 🌱 My current interests are Signal processing, Machine learning, Python, Database, LLM & RAG, MCP & ADK, Multi-Agents, Physical AI, ROS2...

0개의 댓글