Python 멀티스레딩


멀티스레딩은 동시에 여러 프로그램을 실행하는 것과 유사합니다.

  • 스레드를 사용하면 작업을 장기적으로 수행할 수 있습니다. 프로그램을 백그라운드에서 처리합니다.

  • 사용자 인터페이스가 더욱 매력적일 수 있으므로 사용자가 버튼을 클릭하여 특정 이벤트의 처리를 트리거하면 진행률 표시줄이 팝업되어 처리 진행 상황을 표시할 수 있습니다.

  • 프로그램 실행 속도가 빨라질 수 있습니다

  • 스레드는 사용자 입력, 파일 읽기 및 쓰기와 같은 일부 대기 작업을 구현하는 데 더 유용합니다. , 네트워크 전송 및 수신 데이터. 이 경우 메모리 사용량 등과 같은 일부 귀중한 리소스를 해제할 수 있습니다.

스레드는 실행 중인 프로세스와 여전히 다릅니다. 각각의 독립 스레드에는 프로그램 실행을 위한 진입점, 순차적 실행 시퀀스 및 프로그램 종료점이 있습니다. 그러나 스레드는 독립적으로 실행될 수 없으며 응용 프로그램 내에 존재해야 하며 응용 프로그램은 다중 스레드 실행 제어를 제공합니다.

각 스레드에는 스레드 컨텍스트라고 하는 고유한 CPU 레지스터 세트가 있으며, 이는 스레드가 마지막으로 실행되었을 때 CPU 레지스터의 상태를 반영합니다.

명령 포인터와 스택 포인터 레지스터는 스레드 컨텍스트에서 가장 중요한 두 가지 레지스터입니다. 스레드는 항상 프로세스 컨텍스트에서 실행됩니다. 이 주소는 소유한 프로세스의 주소 공간에 메모리를 표시하는 데 사용됩니다. 스레드.

  • 스레드를 선점(중단)할 수 있습니다.

  • 다른 스레드가 실행 중인 동안 스레드가 보류(휴면이라고도 함)될 수 있습니다. 이를 스레드 백오프라고 합니다.


Python 스레드 학습 시작

Python에서 스레드를 사용하는 방법에는 두 가지가 있습니다. 스레드 개체를 래핑하는 함수 또는 클래스입니다.

기능: 스레드 모듈에서 start_new_thread() 함수를 호출하여 새 스레드를 생성합니다. 구문은 다음과 같습니다:

thread.start_new_thread ( function, args[, kwargs] )

매개변수 설명:

  • function - 스레드 함수.

  • args - 스레드 함수에 전달되는 매개변수는 튜플 유형이어야 합니다.

  • kwargs - 선택적 매개변수입니다.

예:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import thread
import time

# 为线程定义一个函数
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print "%s: %s" % ( threadName, time.ctime(time.time()) )

# 创建两个线程
try:
   thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print "Error: unable to start thread"

while 1:
   pass

위 프로그램을 실행한 결과는 다음과 같습니다.

Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009

스레드의 끝은 일반적으로 자연스런 결과에 의존합니다. 스레드 함수의 끝; 스레드에서도 수행할 수 있습니다. 함수에서 thread.exit()가 호출되면 스레드를 종료하기 위해 SystemExit 예외가 발생합니다.


스레딩 모듈

Python은 스레드와 스레딩이라는 두 가지 표준 라이브러리를 통해 스레드를 지원합니다. 스레드는 낮은 수준의 기본 스레드와 간단한 잠금을 제공합니다.

스레드 모듈에서 제공하는 기타 메서드:

  • threading.currentThread(): 현재 스레드 변수를 반환합니다.

  • threading.enumerate(): 실행 중인 스레드가 포함된 목록을 반환합니다. 실행은 시작 전과 종료 후 스레드를 제외하고 시작 후 종료 전 스레드를 나타냅니다.

  • threading.activeCount(): 실행 중인 스레드 수를 반환하며, 이는 len(threading.enumerate())와 동일한 결과를 갖습니다.

스레드 모듈은 스레드를 처리하기 위한 Thread 클래스도 제공합니다.

  • run() : 스레드 활동을 나타내는 데 사용되는 메서드입니다.

  • start():스레드 활동을 시작합니다.


  • Join([time]): 스레드가 종료될 때까지 기다립니다. 이는 스레드의 Join() 메소드가 abort(정상적으로 종료하거나 처리되지 않은 예외 발생)를 호출하거나 선택적 시간 초과가 발생할 때까지 호출 스레드를 차단합니다.

  • isAlive(): 스레드가 활성 상태인지 여부를 반환합니다.

  • getName(): 스레드 이름을 반환합니다.

  • setName(): 스레드 이름을 설정합니다.


Threading 모듈을 사용하여 스레드 생성

Threading 모듈을 사용하여 스레드를 생성하고 threading.Thread에서 직접 상속한 다음 __init__를 재정의합니다. 메서드 및 실행 방법:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time

exitFlag = 0

class myThread (threading.Thread):   #继承父类threading.Thread
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):                   #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 
        print "Starting " + self.name
        print_time(self.name, self.counter, 5)
        print "Exiting " + self.name

def print_time(threadName, delay, counter):
    while counter:
        if exitFlag:
            thread.exit()
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启线程
thread1.start()
thread2.start()

print "Exiting Main Thread"

위 프로그램 실행 결과는 다음과 같습니다.

Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2

스레드 동기화

여러 스레드가 공동으로 특정 데이터를 수정하는 경우 예측할 수 없는 결과가 발생합니다. 데이터의 정확성을 보장하려면 여러 스레드를 동기화해야 합니다.

Thread 객체의 Lock 및 Rlock을 사용하여 간단한 스레드 동기화를 달성할 수 있습니다. 두 객체 모두 한 번에 하나의 스레드만 작동해야 하는 데이터의 경우 획득과 해제 사이에 작업을 배치할 수 있습니다. 행동 양식.

멀티스레딩의 장점은 동시에 여러 작업을 실행할 수 있다는 것입니다(적어도 그렇게 느껴집니다). 그러나 스레드가 데이터를 공유해야 하는 경우 데이터가 동기화되지 않는 문제가 발생할 수 있습니다.

이런 상황을 생각해 보세요. 목록의 모든 요소가 0이고, "set" 스레드는 모든 요소를 ​​뒤에서 앞으로 1로 변경하며, "print" 스레드는 목록을 앞에서 뒤로 읽고, 인쇄.

그런 다음 "set" 스레드가 변경되기 시작하면 "print" 스레드가 목록을 인쇄하고 출력은 0과 1의 절반이 됩니다. 이것이 데이터의 비동기화입니다. 이러한 상황을 방지하기 위해 잠금 개념이 도입되었습니다.

잠금에는 잠김과 잠금 해제의 두 가지 상태가 있습니다. "set"과 같은 스레드가 공유 데이터에 액세스하려고 할 때마다 먼저 잠금을 획득해야 합니다. "print"와 같은 다른 스레드가 이미 잠금을 획득한 경우 "set" 스레드가 일시 중지되도록 해야 합니다. 이는 동기식 차단입니다. 스레드 " Print "가 액세스가 완료되고 잠금이 해제된 후 스레드 "설정"이 계속되도록 합니다.

이러한 처리를 거쳐 목록을 인쇄할 때 모두 0 또는 모두 1이 출력되며, 반은 0이고 반은 1인 민망한 장면은 더 이상 발생하지 않습니다.

예:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time

class myThread (threading.Thread):
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):
        print "Starting " + self.name
       # 获得锁,成功获得锁定后返回True
       # 可选的timeout参数不填时将一直阻塞直到获得锁定
       # 否则超时后将返回False
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # 释放锁
        threadLock.release()

def print_time(threadName, delay, counter):
    while counter:
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

threadLock = threading.Lock()
threads = []

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启新线程
thread1.start()
thread2.start()

# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)

# 等待所有线程完成
for t in threads:
    t.join()
print "Exiting Main Thread"

스레드 우선순위 대기열(Queue)

Python의 대기열 모듈은 FIFO(선입선출)를 포함하여 스레드로부터 안전한 동기식 대기열 클래스를 제공합니다. 대기열 대기열, LIFO(후입선출) 대기열 LifoQueue 및 우선순위 대기열 PriorityQueue. 이러한 대기열은 잠금 기본 형식을 구현하며 다중 스레드에서 직접 사용할 수 있습니다. 대기열을 사용하여 스레드 간 동기화를 달성할 수 있습니다.

큐 모듈의 일반적인 방법:


  • Queue.qsize()는 대기열의 크기를 반환합니다.

  • Queue.empty() 대기열이 비어 있으면 True를 반환하고, 그렇지 않으면 False를 반환합니다

  • Queue.full() 대기열이 가득 차면 True를 반환하고, 그렇지 않으면 False를 반환합니다

  • Queue.full은 최대 크기 크기에 해당합니다

  • Queue.get([block[, timeout]])은 대기열을 가져오고, 시간 초과 대기 시간

  • Queue.get_nowait()은 Queue.get과 동일합니다. (거짓)

  • Queue.put(item)은 대기열에 기록하고 시간 초과 대기 시간

  • Queue.put_nowait(item)은 다음과 같습니다. Queue.put(item, False)

  • Queue.task_done() 작업을 완료한 후 Queue.task_done() 함수는 작업이 완료된 큐에 신호를 보냅니다. 🎜>

  • Queue.join()은 실제로 다른 작업을 수행하기 전에 대기열이 빌 때까지 기다리는 것을 의미합니다.

예:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import Queue
import threading
import time

exitFlag = 0

class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print "Starting " + self.name
        process_data(self.name, self.q)
        print "Exiting " + self.name

def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print "%s processing %s" % (threadName, data)
        else:
            queueLock.release()
        time.sleep(1)

threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1

# 创建新线程
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# 填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# 等待队列清空
while not workQueue.empty():
    pass

# 通知线程是时候退出
exitFlag = 1

# 等待所有线程完成
for t in threads:
    t.join()
print "Exiting Main Thread"

위 프로그램의 실행 결과:

Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread