>백엔드 개발 >파이썬 튜토리얼 >Python 멀티스레딩, 잠금 및 이벤트 이벤트 메커니즘의 간단한 사용에 대한 자세한 설명

Python 멀티스레딩, 잠금 및 이벤트 이벤트 메커니즘의 간단한 사용에 대한 자세한 설명

不言
不言원래의
2018-04-27 11:52:121562검색

이 글에서는 주로 Python 멀티스레딩, 잠금 및 이벤트 이벤트 메커니즘의 간단한 사용법을 소개하고 참고 자료를 제공합니다. 함께 살펴보겠습니다

스레드와 프로세스

1. 스레드는 자신을 생성한 프로세스의 주소 공간을 공유하며, 프로세스는 자체 주소 공간을 갖습니다

2. 프로세스의 데이터와 스레드는 상호 접근이 가능합니다

3. 스레드 간의 데이터는 독립적입니다

4. 자식 프로세스는 스레드의 데이터를 복사합니다

5. 부모 프로세스는 독립적입니다. 프로세스는 하위 프로세스만 종료할 수 있지만 데이터를 교환할 수는 없습니다.

6. 스레드의 데이터를 수정하면 다른 스레드에 영향을 주지만 프로세스를 변경하면 하위 프로세스에는 영향을 미치지 않습니다

threading.Thread

스레딩 모듈에서 가장 중요한 클래스입니다. 하나, 스레드를 생성하는 데 사용할 수 있습니다. 스레드를 생성하는 방법에는 두 가지가 있습니다. 하나는 Thread 클래스를 상속하고 해당 실행 메서드를 재정의하는 것입니다. 다른 하나는 threading.Thread 객체를 생성하고 호출 가능한 객체를 초기화 함수(__init__)의 매개 변수로 전달하는 것입니다.
먼저 threading.Thread 클래스를 상속하여 스레드를 생성하는 예를 살펴보겠습니다.

import threading
import time

class MyThread(threading.Thread):
 def __init__(self, arg):
  # super(MyThread, self).__init__() # 新式类继承原有方法写法
  threading.Thread.__init__(self)
  self.arg = arg

 def run(self):
  time.sleep(2)
  print(self.arg)

for i in range(10):
 thread = MyThread(i)
 print(thread.name)
 thread.start()

스레드를 생성하는 또 다른 방법:

import threading
import time

def process(arg):
 time.sleep(2)
 print(arg)

for i in range(10):
 t = threading.Thread(target=process, args=(i,))
 print(t.name)
 t.start()

Thread 클래스는 다음과 같은 일반적인 메서드도 정의합니다. 및 속성:

Thread.getName() 스레드 이름 가져오기

Thread.setName() 스레드 이름 설정

Thread.name 스레드 이름

Thread.ident 스레드의 식별자를 가져옵니다. 스레드 식별자는 0이 아닌 정수입니다. 이 속성은 start() 메서드가 호출된 후에만 유효합니다. 그렇지 않으면 스레드가 활성 상태인지 확인하기 위해 None

만 반환됩니다. 스레드를 시작하기 위해 start() 메서드가 호출된 시점부터 run() 메서드가 완료되거나 처리되지 않은 예외로 인해 중단될 때까지 스레드는 활성화됩니다

Thread.is_alive()
Thread.isAlive()

Thread .join([timeout]) Thread.join을 호출하면 호출된 스레드의 실행이 완료되거나 시간 초과될 때까지 호출 스레드가 차단됩니다. 매개변수 timeout은 시간 초과 시간을 나타내는 숫자 유형입니다. 이 매개변수가 제공되지 않으면 호출 스레드는 호출된 스레드가 끝날 때까지 차단됩니다.

Python GIL(Global Interpreter Lock)

GIL은 a가 아닙니다. Python 파서(CPython)를 구현할 때 도입한 개념인 Python 의 기능입니다. C++가 언어(문법) 표준 세트와 마찬가지로 다른 컴파일러를 사용하여 실행 가능한 코드로 컴파일될 수 있습니다. GCC, INTEL C++, Visual C++ 등과 같은 유명한 컴파일러. Python의 경우에도 마찬가지입니다. CPython, PyPy 및 Psyco와 같은 다양한 Python 실행 환경을 통해 동일한 코드가 실행될 수 있습니다. 예를 들어 JPython에는 GIL이 없습니다. 그러나 CPython은 대부분의 환경에서 기본 Python 실행 환경입니다. 따라서 많은 사람들의 개념에서는 CPython이 Python이고 GIL이 Python 언어의 결함이라고 당연하게 여깁니다. 따라서 여기서 분명히 짚고 넘어가야 할 점은 GIL은 Python의 기능이 아니며 Python은 GIL에 전혀 의존할 필요가 없다는 것입니다.

스레드 잠금 사용:

# 锁:GIL 全局解释器 它是为了保证线程在运行过程中不被抢占
number = 0
lock = threading.RLock() # 创建锁


def run(num):
 lock.acquire() # 加锁
 global number
 number += 1
 print(number)
 time.sleep(2)
 lock.release() # 释放锁

for i in range(10):
 t = threading.Thread(target=run, args=(i, ))
 t.start()

Join & Daemon

메인 스레드 A에서는 하위 스레드 B가 생성되고, 메인 스레드 A에서는 B.setDaemon()이 호출됩니다. 이때 메인 스레드 A의 실행이 종료되면 하위 스레드 B의 완료 여부와 관계없이 메인 스레드 A와 함께 종료된다는 의미입니다. 기본적으로 Join과 동일한 setDaemon 메소드의 반대입니다. 추가적으로 특별히 주의할 점은 start() 메소드가 호출되기 전에 설정해야 한다는 것입니다. 데몬 스레드로 설정되지 않으면 프로그램이 무기한 정지됩니다.

class MyThread1(threading.Thread):
 def __init__(self):
  threading.Thread.__init__(self)

 def run(self):
  print("thread start")
  time.sleep(3)
  print('thread end')

print('main start')
thread1 = MyThread1()
# thread1.setDaemon(True)  # 设置子线程是否跟随主线程一起结束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print('end join')

def run(n):
 print('[%s]------running----\n' % n)
 time.sleep(2)
 print('--done--')


def main():
 for i in range(5):
  t = threading.Thread(target=run, args=[i,])
  t.start()
  # t.join()
  print('starting thread', t.getName())


m = threading.Thread(target=main,args=[])
# m.setDaemon(True) # 将主线程设置为Daemon线程,它退出时,其它子线程会同时退出,不管是否执行完任务
m.start()
# m.join() # 使主线程阻塞,直至子线程运行完毕再继续主线程
print("---main thread done----")

스레드 잠금(Mutex)

여러 스레드가 하나의 프로세스에서 시작될 수 있으며 여러 스레드가 상위 프로세스의 메모리 공간을 공유하므로 각 스레드가 액세스할 수 있습니다. 이때 두 스레드가 동시에 동일한 데이터를 수정하려고 하면 어떻게 될까요?

num = 100 # 设定一个共享变量
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(2)
 num -= 1 # 对此公共变量进行-1操作
thread_list = []
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)

# 加锁版本
def subNum():
 global num # 在每个线程中都获取这个全局变量
 print('--get num:', num)
 time.sleep(1)
 lock.acquire() # 修改数据前加锁
 num -= 1 # 对此公共变量进行-1操作
 lock.release() # 修改后释放

num = 100 # 设定一个共享变量
thread_list = []
lock = threading.Lock() # 生成全局锁
for i in range(100):
 t = threading.Thread(target=subNum)
 t.start()
 thread_list.append(t)
for t in thread_list: # 等待所有线程执行完毕
 t.join()
print('final num:', num)

Rlock과 Lock의 차이점:

RLock은 동일한 스레드에서 여러 번 획득할 수 있습니다. 하지만 Lock은 이를 허용하지 않습니다. 그렇지 않으면 무한 루프가 발생하고 프로그램은 어떤 잠금을 해제해야 할지 알 수 없습니다. 참고: RLock을 사용하는 경우 acquire와 release가 쌍으로 나타나야 합니다. 즉, acquire를 n번 호출하면 release를 n번 호출해야 점유된 잠금을 실제로 해제할 수 있습니다

Events

Python은 Event 개체를 제공합니다. 스레드 간 통신의 경우 스레드가 설정한 신호 플래그입니다. 신호 플래그가 true인 경우 다른 스레드는 신호에 도달할 때까지 기다립니다.
이벤트 객체는 간단한 스레드 통신 메커니즘을 구현합니다. 스레드 간 통신을 실현하기 위해 신호 설정, 신호 지우기, 대기 등을 제공합니다.

event = threading.Event() 이벤트 만들기

1 신호를 설정합니다.
event.set()

Event의 set() 메서드를 사용하여 Event 개체 내부의 신호 플래그를 true로 설정합니다. Event 객체는 내부 신호 플래그의 상태를 확인하기 위해 isSet() 메서드를 제공합니다.
이벤트 객체의 set() 메소드를 사용하면 isSet() 메소드가 true를 반환합니다

2 Clear signal
event.clear()

使用Event对象的clear()方法可以清除Event对象内部的信号标志,即将其设为假,当使用Event的clear方法后,isSet()方法返回假

3 等待
event.wait()

Event对象wait的方法只有在内部信号为真的时候才会很快的执行并完成返回。当Event对象的内部信号标志位假时,
则wait方法一直等待到其为真时才返回。也就是说必须set新号标志位真

def do(event):
 print('start')
 event.wait()
 print('execute')

event_obj = threading.Event()
for i in range(10):
 t = threading.Thread(target=do, args=(event_obj,))
 t.start()

event_obj.clear()
inp = input('输入内容:')
if inp == 'true':
 event_obj.set()

相关推荐:

Python多线程中阻塞(join)与锁(Lock)使用误区解析

python多线程之事件Event的使用详解

위 내용은 Python 멀티스레딩, 잠금 및 이벤트 이벤트 메커니즘의 간단한 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.