>  기사  >  백엔드 개발  >  파이썬 멀티스레딩

파이썬 멀티스레딩

高洛峰
高洛峰원래의
2016-10-20 11:43:451162검색

우선 멀티스레딩의 적용 시나리오를 설명하겠습니다. Python이 여러 작업을 처리할 때 이러한 작업은 본질적으로 비동기식이며 여러 동시 트랜잭션이 필요하며 각 트랜잭션의 실행 순서는 불확실하고 무작위적이며 예측할 수 없습니다. 예측. 계산 집약적인 작업은 여러 하위 작업으로 순차적으로 실행되거나 다중 스레드 방식으로 처리될 수 있습니다. 그러나 I/O 집약적인 작업은 단일 스레드 방식으로 처리하기 어렵습니다. 멀티 스레드를 사용하지 않으면 하나 이상의 타이머로만 처리할 수 있습니다.

프로세스와 스레드에 대해 이야기해 보겠습니다. 프로세스(때때로 헤비웨이트 프로세스라고도 함)는 프로그램 실행입니다. CentOS에서 ps -aux | 프로세스, 즉 이 grep 프로세스는 각 프로세스마다 자체 주소 공간, 메모리, 데이터 스택 및 실행 궤적을 기록하는 기타 보조 데이터를 갖습니다. 따라서 각 프로세스는 직접 정보를 공유할 수 없으며 프로세스 간 통신만 사용할 수 있습니다. IPC).

스레드(경량 프로세스)와 프로세스의 가장 큰 차이점은 모든 스레드가 동일한 프로세스에서 실행되고, 동일한 운영 환경을 공유하며, 동일한 데이터 공간을 공유한다는 것입니다. 따라서 스레드는 프로세스보다 더 편리하게 서로 데이터를 공유하고 통신할 수 있으며 동시에 트랜잭션을 실행할 수 있습니다.

메모리 프로세스와 스레드의 관계를 쉽게 이해하기 위해 비유를 할 수 있습니다. CPU를 이삿짐 회사에 비유하면 이 이삿짐 회사는 사용할 차량(프로세스)이 하나만 있습니다. 이 회사는 아주 형편없고 직원도 1명(싱글쓰레드)뿐인데, 그 회사는 하루에 5채까지만 이사를 할 수 있다고 해서 나중에 사장이 차를 사는 대신 n명을 더 고용했다. 직원들(추가 스레드) 이런 식으로 직원 한 명씩 한 집씩만 이동한 뒤 쉬고 차를 포기하고 다른 사람들은 다음 집으로 이동하도록 하는 방식이 될 것 같지 않습니다. 실제로 효율성은 향상되지만 비용은 증가합니다. 그렇죠? 이는 GIL(Global Interpreter Lock) 전역 인터프리터 잠금이 스레드 안전성(데이터를 안전하게 읽을 수 있도록 보장), 즉 한 번에 하나의 스레드만 CPU에서 실행될 수 있기 때문입니다. 동시에 이것은 Python의 고유한 메커니즘입니다. 즉, 실행 환경에 이중 CPU가 있어도 Python 가상 머신은 하나의 CPU만 사용합니다. 이는 GIL이 CPython에서 성능을 직접 사용할 수 없음을 의미합니다. 작업 속도를 높이기 위한 물리적 멀티 코어. 자세한 설명(역사에서 남은 문제, 하드웨어 개발이 너무 빠르다)은 다음 블로그를 참조하세요:

http://blog.sina.com.cn/s/blog_64ecfc2f0102uzzf.html

in Python 코어 프로그래밍에서 저자는 다음과 같은 이유로 스레드 모듈이 아닌 스레드 모듈을 사용할 것을 강력히 권장합니다.

1. 메인 스레드가 종료되면 다른 모든 스레드도 종료됩니다. 지워지지 않고 스레드 모듈은 모든 하위 스레드의 안전한 종료를 보호할 수 없습니다. 즉, 스레드 모듈은 데몬을 지원하지 않습니다.

2. 스레드 모듈의 속성이 스레딩과 충돌할 수 있습니다.

3. 하위 수준 스레드 모듈에는 동기화 기본 요소가 거의 없습니다(실제로 절전 모드여야 하는 하나만 있습니다).

1. 스레드 모듈

다음은 GIL을 사용하지 않고 GIL을 사용하는 두 가지 샘플 코드입니다.

1. GIL을 사용하지 않는 코드 샘플:

from time import sleep,ctime
import thread

def loop0():
    print 'start loop 0 at: ',ctime()
    sleep(4)
    print 'loop 0 done at: ',ctime()
def loop1():
    print 'start loop 1 at: ',ctime()
    sleep(2)
    print 'loop 1 done at: ',ctime()
def main():
    print 'start at: ',ctime()
    thread.start_new_thread(loop0,())
    thread.start_new_thread(loop1,())
    sleep(6)
    print 'all loop is done, ' ,ctime()

if __name__=='__main__':
    main()
 

输出结果:

start at:  Thu Jan 28 10:46:27 2016
start loop 0 at:   Thu Jan 28 10:46:27 2016

start loop 1 at:   Thu Jan 28 10:46:27 2016
loop 1 done at:  Thu Jan 28 10:46:29 2016
loop 0 done at:  Thu Jan 28 10:46:31 2016
all loop is done,  Thu Jan 28 10:46:33 2016

위 출력에서 ​​볼 수 있듯이 두 개의 스레드를 성공적으로 시작하고 이를 메인 스레드와 동기화했습니다. 2초에 loop1이 먼저 완료되고, 4초에 loop0이 완료되었으며, 또 다른 2초 후에 메인 스레드가 완료되었습니다. 전체 메인 스레드가 6초를 경과했으며, loop0과 loop1이 동시에 완료됩니다.

2. GIL을 사용한 코드 예:

import thread
from time import sleep,ctime
loops = [4,2]
def loop(nloop,nsec,lock):
    print 'start loop',nloop,'at: ',ctime()
    sleep(nsec)
    print 'loop',nloop,'done at:',ctime()
    lock.release()
def main():
    print 'starting at:',ctime()
    locks = []
    nloops = range(len(loops))

    for i in nloops:
        lock = thread.allocate_lock()                          #创建锁的列表,存在locks中
        lock.acquire()                         
        locks.append(lock)                                      
    for i in nloops:
        thread.start_new_thread(loop,(i,loops[i],locks[i]))    #创建线程,参数为循环号,睡眠时间,锁
    for i in nloops:
        while locks[i].locked():                              #等待循环完成,解锁
            pass
    print 'all DONE at:',ctime()
if __name__ == '__main__':
    main()
 

以上输出如下:

starting at: Thu Jan 28 14:59:22 2016
start loop  0  at:   Thu Jan 28 14:59:22 2016

start loop  1  at:   Thu Jan 28 14:59:22 2016
loop 1 done at: Thu Jan 28 14:59:24 2016
loop 0 done at: Thu Jan 28 14:59:26 2016
all DONE at: Thu Jan 28 14:59:26 2016

4초 동안 지속되어 효율성이 향상되고 메인 스레드에서 sleep() 함수를 사용하는 것보다 낫습니다. 시간을 맞추는 것이 더 합리적입니다.

2. Threading 모듈

1. Thread 클래스

thread 클래스에서는 다음 세 가지 메소드를 사용하여 스레드를 생성할 수 있습니다.

(1) 스레드 인스턴스를 생성하고 함수에 전달

(2) 스레드 인스턴스를 생성하고 호출 가능한 클래스 객체에 전달

(3) 스레드에서 하위 클래스 파생 그리고 이 하위 클래스의 객체를 생성합니다

방법 (1)

__author__ = 'dell'
import threading
from time import sleep,ctime
def loop0():
    print 'start loop 0 at:',ctime()
    sleep(4)
    print 'loop 0 done at:',ctime()
def loop1():
    print 'start loop 1 at:',ctime()
    sleep(2)
    print 'loop 1 done at:',ctime()
def main():
    print 'starting at:',ctime()
    threads = []
    t1 = threading.Thread(target=loop0,args=())          #创建线程
    threads.append(t1)
    t2 = threading.Thread(target=loop1,args=())
    threads.append(t2)
    for t in threads:
        t.setDaemon(True)<span style="white-space:pre">    </span>      #开启守护线程(一定要在start()前调用)
        t.start()<span style="white-space:pre">        </span>      #开始线程执行
    for t in threads:<span style="white-space:pre">                    </span>
        t.join()<span style="white-space:pre">        </span>      #将程序挂起阻塞,直到线程结束,如果给出数值,则最多阻塞timeout秒

if __name__ == &#39;__main__&#39;:
    main()
    print &#39;All DONE at:&#39;,ctime()

在这里,就不用像thread模块那样要管理那么多锁(分配、获取、释放、检查等)了,同时我也减少了循环的代码,直接自己编号循环了,得到输出如下:
 

starting at: Thu Jan 28 16:38:14 2016
start loop 0 at: Thu Jan 28 16:38:14 2016
start loop 1 at: Thu Jan 28 16:38:14 2016
loop 1 done at: Thu Jan 28 16:38:16 2016
loop 0 done at: Thu Jan 28 16:38:18 2016
All DONE at: Thu Jan 28 16:38:18 2016

결과는 동일하지만 코드 논리로 보면 훨씬 더 명확합니다. 다른 두 가지 유형은 여기에 게시되지 않습니다. Thread를 인스턴스화하는 것과 thread.start_new_thread를 호출하는 것의 가장 큰 차이점은 새 스레드가 즉시 실행을 시작하지 않는다는 것입니다. 즉, 스레딩 모듈의 Thread 클래스에서 인스턴스화한 후 . start() 함수를 실행하면 프로그램이 좋은 동기화 특성을 갖게 됩니다.

다음은 단일 스레드와 멀티 스레드의 비교 예입니다. 두 세트의 연산이 각각 곱셈과 나눗셈으로 완료되어 멀티 스레드에 의한 효율성 향상을 보여줍니다.

from time import ctime,sleep
import threading

def multi():
    num1 = 1
    print &#39;start mutiple at:&#39;,ctime()
    for i in range(1,10):
       num1 = i*num1
       sleep(0.2)
    print &#39;mutiple finished at:&#39;,ctime()
    return num1
def divide():
    num2 = 100
    print &#39;start division at:&#39;,ctime()
    for i in range(1,10):
        num2 = num2/i
        sleep(0.4)
    print &#39;division finished at:&#39;,ctime()
    return num2
def main():
    print &#39;---->single Thread&#39;
    x1 = multi()
    x2 = divide()
    print &#39;The sum is &#39;,sum([x1,x2]),&#39;\nfinished singe thread&#39;,ctime()

    print &#39;----->Multi Thread&#39;
    threads = []
    t1 = threading.Thread(target=multi,args=())
    threads.append(t1)
    t2 = threading.Thread(target=divide,args=())
    threads.append(t2)
    for t in threads:
        t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()

if __name__ == &#39;__main__&#39;:
    main()

结果如下:

 

---->single Thread

start mutiple at: Thu Jan 28 21:41:18 2016

mutiple finished at: Thu Jan 28 21:41:20 2016

start division at: Thu Jan 28 21:41:20 2016

division finished at: Thu Jan 28 21:41:24 2016

The sum is  362880 

finished singe thread Thu Jan 28 21:41:24 2016

----->Multi Thread

start mutiple at: Thu Jan 28 21:41:24 2016

start division at: Thu Jan 28 21:41:24 2016

mutiple finished at: Thu Jan 28 21:41:26 2016

division finished at: Thu Jan 28 21:41:27 2016

The sum is : 362880


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