파이썬 스레딩: 소개 – koreantech.org

이 자습서에서는 Python의 내장 스레딩 모듈을 사용하여 Python의 다중 스레딩 기능을 탐색하는 방법을 배웁니다.

프로세스 및 스레드의 기본부터 시작하여 동시성 및 병렬 처리의 개념을 이해하면서 Python에서 멀티스레딩이 작동하는 방식을 배우게 됩니다. 그런 다음 내장 스레딩 모듈을 사용하여 Python에서 하나 이상의 스레드를 시작하고 실행하는 방법을 배웁니다.

시작하자.

프로세스 대 스레드: 차이점은 무엇입니까?

프로세스란?

프로세스는 실행해야 하는 프로그램의 모든 인스턴스입니다.

Python 스크립트 또는 Chrome과 같은 웹 브라우저에서 화상 회의 애플리케이션에 이르기까지 무엇이든 될 수 있습니다. 컴퓨터에서 작업 관리자를 시작하고 성능 -> CPU로 이동하면 현재 CPU 코어에서 실행 중인 프로세스와 스레드를 볼 수 있습니다.

프로세스 및 스레드 이해

내부적으로 프로세스에는 해당 프로세스에 해당하는 코드와 데이터를 저장하는 전용 메모리가 있습니다.

프로세스는 하나 이상의 스레드로 구성됩니다. 스레드는 운영 체제가 실행할 수 있는 가장 작은 명령 시퀀스이며 실행 흐름을 나타냅니다.

각 스레드에는 자체 스택과 레지스터가 있지만 전용 메모리는 없습니다. 프로세스와 연결된 모든 스레드는 데이터에 액세스할 수 있습니다. 따라서 데이터와 메모리는 프로세스의 모든 스레드에서 공유됩니다.

N개의 코어가 있는 CPU에서 N개의 프로세스는 동일한 시간에 병렬로 실행할 수 있습니다. 그러나 동일한 프로세스의 두 스레드는 병렬로 실행할 수 없지만 동시에 실행할 수 있습니다. 다음 섹션에서 동시성 대 병렬성의 개념을 다룰 것입니다.

지금까지 배운 내용을 바탕으로 프로세스와 스레드의 차이점을 요약해 보겠습니다.

FeatureProcessThreadMemoryDedicated memoryShared memoryMode of executionParallel, concurrentConcurrent; 그러나 운영 체제CPython 인터프리터에서 처리하는 parallelExecution은 아닙니다.

파이썬의 멀티스레딩

Python에서 GIL(Global Interpreter Lock)은 한 스레드만 잠금을 획득하고 특정 시점에서 실행할 수 있도록 합니다. 모든 스레드가 실행하려면 이 잠금을 획득해야 합니다. 이렇게 하면 특정 시점에서 단일 스레드만 실행될 수 있고 동시 멀티스레딩이 방지됩니다.

예를 들어, 동일한 프로세스의 두 스레드, t1 및 t2를 고려하십시오. 스레드는 t1이 특정 값 k를 읽을 때 동일한 데이터를 공유하기 때문에 t2는 동일한 값 k를 수정할 수 있습니다. 이는 교착 상태 및 바람직하지 않은 결과를 초래할 수 있습니다. 그러나 스레드 중 하나만 잠금을 획득하고 모든 인스턴스에서 실행할 수 있습니다. 따라서 GIL은 스레드 안전성도 보장합니다.

그렇다면 파이썬에서 멀티스레딩 기능을 어떻게 달성할 수 있을까요? 이를 이해하기 위해 동시성과 병렬성의 개념에 대해 논의해 보겠습니다.

동시성 대 병렬성: 개요

하나 이상의 코어가 있는 CPU를 고려하십시오. 아래 그림에서 CPU에는 4개의 코어가 있습니다. 이는 주어진 순간에 4개의 다른 작업을 병렬로 실행할 수 있음을 의미합니다.

4개의 프로세스가 있는 경우 각 프로세스는 4개의 코어 각각에서 독립적으로 동시에 실행할 수 있습니다. 각 프로세스에 두 개의 스레드가 있다고 가정해 보겠습니다.

스레딩이 작동하는 방식을 이해하기 위해 멀티코어에서 단일 코어 프로세서 아키텍처로 전환해 보겠습니다. 언급했듯이 특정 실행 인스턴스에서 단일 스레드만 활성화될 수 있습니다. 그러나 프로세서 코어는 스레드 간에 전환할 수 있습니다.

예를 들어, I/O 바운드 스레드는 종종 사용자 입력 읽기, 데이터베이스 읽기 및 파일 작업과 같은 I/O 작업을 기다립니다. 이 대기 시간 동안 다른 스레드가 실행될 수 있도록 잠금을 해제할 수 있습니다. 대기 시간은 n초 동안 잠자기와 같은 간단한 작업일 수도 있습니다.

요약: 대기 작업 중에 스레드는 잠금을 해제하여 프로세서 코어가 다른 스레드로 전환할 수 있도록 합니다. 대기 기간이 완료된 후 이전 스레드가 실행을 재개합니다. 프로세서 코어가 스레드 간에 동시에 전환하는 이 프로세스는 멀티스레딩을 용이하게 합니다. ✅

애플리케이션에서 프로세스 수준 병렬 처리를 구현하려면 대신 다중 처리를 사용하는 것이 좋습니다.

Python 스레딩 모듈: 첫 번째 단계

Python은 Python 스크립트로 가져올 수 있는 스레딩 모듈과 함께 제공됩니다.

import threading

파이썬에서 스레드 객체를 생성하기 위해 스레드 생성자를 사용할 수 있습니다: threading.Thread(…). 이것은 대부분의 스레딩 구현에 충분한 일반 구문입니다.

threading.Thread(target=...,args=...)

여기,

  • target은 Python 호출 가능을 나타내는 키워드 인수입니다.
  • args는 대상이 받는 인수의 튜플입니다.

이 자습서의 코드 예제를 실행하려면 Python 3.x가 필요합니다. 코드를 다운로드하고 따라해보세요.

Python에서 스레드를 정의하고 실행하는 방법

대상 함수를 실행하는 스레드를 정의해 보겠습니다.

대상 함수는 some_func입니다.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())

위의 코드 조각이 하는 일을 구문 분석해 보겠습니다.

  • 스레딩 및 시간 모듈을 가져옵니다.
  • 함수 some_func에는 설명적인 print() 문이 있으며 2초 동안 휴면 작업을 포함합니다. time.sleep(n)은 함수를 n초 동안 휴면 상태로 만듭니다.
  • 다음으로 target이 some_func인 스레드 thread_1을 정의합니다. threading.Thread(target=…)는 스레드 객체를 생성합니다.
  • 참고: 함수 호출이 아닌 함수 이름을 지정하십시오. some_func()가 아닌 some_func를 사용하십시오.
  • 스레드 개체를 생성하면 스레드가 시작되지 않습니다. 스레드 개체에서 start() 메서드를 호출하면 됩니다.
  • 활성 스레드 수를 얻으려면 active_count() 함수를 사용합니다.

Python 스크립트는 메인 스레드에서 실행 중이며, 출력에서 ​​볼 수 있듯이 활성 스레드 수가 2가 되도록 some_func 함수를 실행하기 위해 다른 스레드(thread1)를 생성합니다.

# Output
Running some_func...
2
Finished running some_func.

출력을 자세히 살펴보면 thread1을 시작할 때 첫 번째 print 문이 실행된다는 것을 알 수 있습니다. 그러나 슬립 작업 동안 프로세서는 메인 스레드로 전환하고 스레드 1이 실행을 마칠 때까지 기다리지 않고 활성 스레드 수를 출력합니다.

스레드가 실행을 마칠 때까지 기다리기

thread1이 실행을 끝내도록 하려면 스레드를 시작한 후 join() 메서드를 호출하면 됩니다. 그렇게 하면 메인 스레드로 전환하지 않고 thread1이 실행을 마칠 때까지 기다립니다.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())

이제 활성 스레드 수를 인쇄하기 전에 thread1의 실행이 완료되었습니다. 따라서 메인 스레드만 실행 중이며, 이는 활성 스레드 수가 1개임을 의미합니다. ✅

# Output
Running some_func...
Finished running some_func.
1

Python에서 여러 스레드를 실행하는 방법

다음으로 두 개의 다른 기능을 실행하는 두 개의 스레드를 만들어 보겠습니다.

여기서 count_down은 숫자를 인수로 받아 그 숫자에서 0까지 카운트다운하는 함수입니다.

def count_down(n):
    for i in range(n,-1,-1):
        print(i)

우리는 0에서 주어진 숫자까지 세는 또 다른 Python 함수인 count_up을 정의합니다.

def count_up(n):
    for i in range(n+1):
        print(i)

📑 range() 함수를 range(start, stop, step) 구문으로 사용할 경우 기본적으로 종점 stop은 제외됩니다.

– 특정 숫자에서 0으로 카운트다운하려면 음수 단계 값 -1을 사용하고 정지 값을 -1로 설정하여 0이 포함되도록 할 수 있습니다.

– 마찬가지로 n까지 세려면 stop 값을 n + 1로 설정해야 합니다. start와 step의 기본값은 각각 0과 1이므로 range(n + 1)을 사용하여 시퀀스 0을 얻을 수 있습니다. n을 통해

다음으로, count_down 및 count_up 함수를 각각 실행하기 위해 두 개의 스레드(thread1 및 thread2)를 정의합니다. 두 함수 모두에 대해 인쇄 문과 절전 작업을 추가합니다.

스레드 객체를 생성할 때 대상 함수에 대한 인수는 args 매개변수에 대한 튜플로 지정되어야 합니다. 두 함수(count_down 및 count_up)가 하나의 인수를 취하기 때문입니다. 값 뒤에 명시적으로 쉼표를 삽입해야 합니다. 이렇게 하면 후속 요소가 None으로 유추되기 때문에 인수가 여전히 튜플로 전달됩니다.

import threading
import time

def count_down(n):
    for i in range(n,-1,-1):
        print("Running thread1....")
        print(i)
        time.sleep(1)


def count_up(n):
    for i in range(n+1):
        print("Running thread2...")
        print(i)
        time.sleep(1)

thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()

출력에서:

  • count_up 함수는 thread2에서 실행되며 0에서 시작하여 최대 5까지 계산합니다.
  • count_down 함수는 thread1에서 실행되며 10에서 0까지 카운트다운합니다.
# Output
Running thread1....
10
Running thread2...
0
Running thread1....
9
Running thread2...
1
Running thread1....
8
Running thread2...
2
Running thread1....
7
Running thread2...
3
Running thread1....
6
Running thread2...
4
Running thread1....
5
Running thread2...
5
Running thread1....
4
Running thread1....
3
Running thread1....
2
Running thread1....
1
Running thread1....
0

스레드 1과 스레드 2는 모두 대기 작업(수면)을 포함하므로 번갈아 실행되는 것을 볼 수 있습니다. count_up 함수가 5까지 카운트를 마치면 thread2는 더 이상 활성화되지 않습니다. 따라서 우리는 thread1에만 해당하는 출력을 얻습니다.

합산

이 자습서에서는 Python의 내장 스레딩 모듈을 사용하여 멀티스레딩을 구현하는 방법을 배웠습니다. 주요 내용을 요약하면 다음과 같습니다.

  • Thread 생성자는 스레드 개체를 만드는 데 사용할 수 있습니다. threading.Thread(target=,args=())를 사용하면 args에 지정된 인수로 대상 호출 가능을 실행하는 스레드가 생성됩니다.
  • Python 프로그램은 기본 스레드에서 실행되므로 생성한 스레드 개체는 추가 스레드입니다. active_count() 함수를 호출하면 모든 인스턴스에서 활성 스레드 수를 반환합니다.
  • 스레드 개체에서 start() 메서드를 사용하여 스레드를 시작하고 join() 메서드를 사용하여 실행이 완료될 때까지 기다릴 수 있습니다.

대기 시간을 조정하고 다른 I/O 작업을 시도하는 등의 방법으로 추가 예제를 코딩할 수 있습니다. 향후 Python 프로젝트에서 멀티스레딩을 구현해야 합니다. 즐거운 코딩!🎉