【python】多线程threading详解

1、多线程的理解

多进程和多线程都可以执行多个任务,线程是进程的一部分。线程的特点是线程之间可以共享内存和变量,资源消耗少,缺点是线程之间的同步和加锁比较麻烦。

一般线程的运行逻辑,如图:
这里写图片描述

  1. 新建:使用线程的第一步就是创建线程,创建后的线程只是进入可执行的状态,也就是Runnable
  2. Runnable:进入此状态的线程还并未开始运行,一旦CPU分配时间片给这个线程后,该线程才正式的开始运行
  3. Running:线程正式开始运行,在运行过程中线程可能会进入阻塞的状态,即Blocked
  4. Blocked:在该状态下,线程暂停运行,解除阻塞后,线程会进入Runnable状态,等待CPU再次分配时间片给它
  5. 结束:线程方法执行完毕或者因为异常终止返回

其中最复杂的是线程从Running进入Blocked状态,通常有三种情况:

  • 睡眠:线程主动调用sleep()join()方法后.
  • 等待:线程中调用wait()方法,此时需要有其他线程通过notify()方法来唤醒
  • 同步:线程中获取线程锁,但是因为资源已经被其他线程占用时.

2、Python多线程创建

在Python中,同样可以实现多线程,有两个标准模块thread和threading,不过我们主要使用更高级的threading模块。

实例:
import threading
import time
#定义线程需要做的内容,写在函数里面
def target():
    print('当前的线程%s 在运行' % threading.current_thread().name)
    time.sleep(1)
    print('当前的线程 %s 结束' % threading.current_thread().name)

print('当前的线程 %s 在运行' % threading.current_thread().name)
t = threading.Thread(target=target,args = [])

t.start()  #线程启动
print('当前的线程 %s 结束' % threading.current_thread().name)
输出:
当前的线程 MainThread 在运行
当前的线程Thread-13 在运行
当前的线程 MainThread 结束
当前的线程 Thread-13 结束

从结果可以看出,主线程结束后,子线程还没有运行结束;这时候为了让子进程先与主进程结束,可以调用join( )函数;
join( )函数:join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止。
join有一个timeout参数

  1. 当设置守护线程时【setDaemon(True)】,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
  2. 没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。
import threading
import time

def target():
    print('当前的线程%s 在运行' % threading.current_thread().name)
    time.sleep(1)
    print('当前的线程 %s 结束' % threading.current_thread().name)

print('当前的线程 %s 在运行' % threading.current_thread().name)
t = threading.Thread(target=target,args = [])

t.start()
t.join() #阻塞进程
print('当前的线程 %s 结束' % threading.current_thread().name)

添加join( )阻塞进程后输出:

当前的线程 MainThread 在运行
当前的线程Thread-11 在运行
当前的线程 Thread-11 结束
当前的线程 MainThread 结束

但如果为线程实例添加t.setDaemon(True)守护进程之后,如果不加join语句,那么当主线程结束之后,会杀死子线程。如果加上join,并设置等待时间,就会等待线程一段时间再退出:

首先看一下守护进程机制:

import time

def run():

    time.sleep(2)
    print('当前线程的名字是: ', threading.current_thread().name)
    time.sleep(2)


if __name__ == '__main__':

    start_time = time.time()

    print('这是主线程:', threading.current_thread().name)
    thread_list = []
    for i in range(5):
        t = threading.Thread(target=run)
        thread_list.append(t)

    for t in thread_list:
        t.setDaemon(True)
        t.start()

    print('主线程结束了!' , threading.current_thread().name)
    print('一共用时:', time.time()-start_time)
'''
这是主线程: MainThread
主线程结束了! MainThread
一共用时: 0.015624046325683594
当前线程的名字是: 当前线程的名字是:  Thread-30
当前线程的名字是:  Thread-29
 Thread-31
当前线程的名字是:  Thread-33
当前线程的名字是:  Thread-32
'''

可以看出当设置守护进程t.setDaemon(True)后,主进程结束就结束了整个进程,子进程依旧打印。

然后加上join( ),如果加上join,并设置等待时间,就会等待线程一段时间再退出

import threading

import time

def target():
    print('当前的线程%s 在运行' % threading.current_thread().name)
    time.sleep(4)
    print('当前的线程 %s 结束' % threading.current_thread().name)

print('当前的线程 %s 在运行' % threading.current_thread().name)
t = threading.Thread(target=target,args = [])

t.setDaemon(False)
t.start()
t.join(5)  #为子线程设定运行的时间,5s后就退出子线程,如果不加等待时间,就会一直等待子线程运行结束后,再运行主线程
print('当前的线程 %s 结束' % threading.current_thread().name)
'''
当前的线程 MainThread 在运行
当前的线程Thread-35 在运行
当前的线程 Thread-35 结束
当前的线程 MainThread 结束
'''

3、线程锁和ThreadLocal

在threading中提供RLock对象,RLock对象内部维护着一个Lock对象,它是一种可重入锁。对于Lock对象而言,如果一个线程连续
两次release,使得线程死锁。而RLock对象允许一个线程多次对其进行acquire操作,因为在其内部通过一个counter变量维护着
线程acquire的次数。而且每一次的acquire操作必须有一个release操作与之对应,在所有的release操作完成之后,别的线程才
能申请该RLock对象.
import threading
mylock = threading.RLock()
num = 0
class WorkThread(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.t_name = name
    def run(self):
        global num
        while True:
            mylock.acquire()
            print('\n%s locked, number: %d' % (self.t_name, num))
            if num >= 2:
                mylock.release()
                print('\n%s released, number: %d' % (self.t_name, num))
                break
            num += 1
            print('\n%s released, number: %d' % (self.t_name, num))
            mylock.release()
def test():
    thread1 = WorkThread('A-Worker')
    thread2 = WorkThread('B-Worker')
    thread1.start()
    thread2.start()
if __name__ == '__main__':
    test() 

输出结果:

A-Worker locked, number: 0
A-Worker released, number: 1
A-Worker locked, number: 1
A-Worker released, number: 2
A-Worker locked, number: 2
A-Worker released, number: 2
B-Worker locked, number: 2
B-Worker released, number: 2
介绍完线程锁,接下来出场的是ThreadLocal。当不想将变量共享给其他线程时,可以使用局部变量,但在函数中定义局部变量会使
得在函数之间传递特别麻烦。ThreadLocal是非常牛逼的东西,它解决了全局变量需要枷锁,局部变量传递麻烦的两个问题。通过在
线程中定义:local_ = threading.local()此时这个local_就变成了一个全局变量,但这个全局变量只在该线程中为全局变量,
对于其他线程来说是局部变量,别的线程不可更改。  
import threading
local = threading.local()
def func(name):
    print('current thread:%s' % threading.currentThread().name)
    local.name = name
    print("%s in %s" % (local.name,threading.currentThread().name))
t1 = threading.Thread(target=func,args=('haibo',))
t2 = threading.Thread(target=func,args=('lina',))
t1.start()
t2.start()
t1.join()
t2.join()

输出:

current thread:Thread-47
haibo in Thread-47
current thread:Thread-48
lina in Thread-48

猜你喜欢

转载自blog.csdn.net/brucewong0516/article/details/81028716
今日推荐