多进程(mutiprocessing)与多线程(Threading)之多线程

时间:2021-01-10 16:38:39

多线程(threading)

多线程与多进程其实大同小异,他们有很多方法从名字到功能都是一样,比如都有start(),join(),都有守护线程/进程deamon.

一个简单的栗子:

import threading
import os,time

def loop():
    for i in range(5):        
        t = threading.Thread(name=str(i))        
        t.start()
        print('thread %s is running!'%t.name)
        print('thread %s exited!'%t.name)
        t.join()
        

if __name__ == "__main__":
    print('thread %s is running!'%threading.current_thread().name)
    T = threading.Thread(target=loop)
    T.start()
    T.join()
    print('thread %s exited'%threading.current_thread().name)

output:

多进程(mutiprocessing)与多线程(Threading)之多线程

由于Python的多线程并不是真正意义上充分利用多核性能的多线程,它是只是实现了单核并发(详见并发与并行),也就是说,处理多线程时,

每个线程执行一部分代码,然后执行下一个线程的代码,所以稍不留神,输出就会跟设计的初衷相左.

 

import  threading
import time

num = 0

def change(n):
    #声明全局变量num
    global num
    #进行加减n
    num = num + n
    num = num - n

def run_thread(n):
    #调用change方法100万次
    for i in range(1000000):
        change(n)

if __name__ == "__main__":
    t1 = threading.Thread(target=run_thread,args=(5,))
    t2 = threading.Thread(target=run_thread,args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(num)

 

output:

第一次:

多进程(mutiprocessing)与多线程(Threading)之多线程

第二次:

多进程(mutiprocessing)与多线程(Threading)之多线程

第三次:

多进程(mutiprocessing)与多线程(Threading)之多线程

会得出三次不同的结果,事实上,如果增大调用change的次数,结果会更显著.

原因是多线程间的变量是共享的.num在被线程t1,t2交替使用.

语句num = num + n其实分作两步:

先计算num+n的值存入临时变量

然后把临时变量赋值给num

但在多线程中,尤其是运算量大的地方,并不能保证这两步的连贯执行,因此就不能保证结果的准确性.

因此我们需要引入多线程特有的类:锁!

Lock

Lock类只有两个方法:

acquire(blocking=Truetimeout=-1)

这是一个等待锁的方法,可选参数也是我们的老朋友,堵塞与超时

release()释放锁的方法,一个函数获取了锁之后一定要释放锁,否则还在等待锁的程序望穿眼也等不到锁,成为幽灵线程.

所以,上述的代码要这样优化:

import  threading
import time

num = 0
lock = threading.Lock()
def change(n):
    #声明全局变量num
    global num
    #进行加减n
    num = num + n
    num = num - n
    

def run_thread(n):
    #调用change方法100万次
    for i in range(1000000):
        #获取锁        
        lock.acquire()
        try:
            change(n)
        finally:
            #最后必定要释放锁
            lock.release()

if __name__ == "__main__":    
    t1 = threading.Thread(target=run_thread,args=(5,))
    t2 = threading.Thread(target=run_thread,args=(8,))    
    t1.start()    
    t2.start()
    t1.join()
    t2.join()
    print(num)

这样无论怎么如何,结果都错不了了!

最后的最后,由于Python GIL(全局锁)的存在,任何Python线程执行前,线程都要获得GIL锁,然后没执行100条字节码,自动释放GIL锁,然后去执行其他的线程任务,

所以Python的多线程并不能利用多核.所以要想利用多核,还是好好用多进程吧!

 

Local:

threading库下的local类,实现了各线程间的数据独立.

import threading

#创建全局local对象
local = threading.local()

def set_std():
    #获取与当前线程关联的student
    std = local.student
    print('I am %s in thread %s'%(std,threading.current_thread().name))

#在这个函数准备好后面需要的参数,如本例中的student
def set_thr(name): local.student = name set_std() if __name__ == "__main__": t1 = threading.Thread(target=set_thr,args=("你爹",),name="No.1") t2 = threading.Thread(target=set_thr,args=("你妈",),name="No.2") t1.start() t2.start() t1.join() t2.join()

output:

多进程(mutiprocessing)与多线程(Threading)之多线程

 

文章参考了廖雪峰廖大的教程,感谢廖大!