1.1 什么是多线程 Threading

同时分批做同一件事情,以提高运算效率

1.2 添加线程 Thread

添加线程

本节我们来学习threading模块的一些基本操作,如获取线程数,添加线程等。首先别忘了导入模块:

import threading

获取已激活的线程数

threading.active_count()
# 2

查看所有线程信息

threading.enumerate()
# [<_MainThread(MainThread, started 140736011932608)>, <Thread(SockThread, started daemon 123145376751616)>]

输出的结果是一个<_MainThread(...)>带多个<Thread(...)>

查看现在正在运行的线程

threading.current_thread()
# <_MainThread(MainThread, started 140736011932608)>

添加线程,threading.Thread()接收参数target代表这个线程要完成的任务,需自行定义

def thread_job():
    print(\'This is a thread of %s\' % threading.current_thread())

def main():
    thread = threading.Thread(target=thread_job,)   # 定义线程 
    thread.start()  # 让线程开始工作

完整程序与运行结果

import threading

def thread_job():
    print(\'This is an added Thread,number is %s\'% threading.current_thread())

def main ():
    added_thread=threading.Thread(target=thread_job)
    added_thread.start()
    print(threading.active_count())
    print(threading.enumerate())
    print(threading.current_thread())

if __name__==\'__main__\':
    main()
\"\"\"
This is an added Thread,number is <Thread(Thread-1, started 6904)>
2
[<_MainThread(MainThread, started 12484)>, <Thread(Thread-1, started 6904)>]
<_MainThread(MainThread, started 12484)>
\"\"\"

1.3 join 功能

  • 当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束.
  • 当我们使用setDaemon(True)方法,设置子线程为守护线程时,主线程一旦执行结束,则全部线程全部被终止执行,可能出现的情况就是,子线程的任务还没有完全执行结束,就被迫停止。
  • 此时join的作用就凸显出来了,join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程在终止。
  • join有一个timeout参数:当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。

不加 join() 的结果

我们让T1线程工作的耗时增加.

import threading
import time

def thread_job():
    print(\"T1 start\\n\")
    for i in range(10):
        time.sleep(0.1) # 任务间隔0.1s
    print(\"T1 finish\\n\")

added_thread = threading.Thread(target=thread_job, name=\'T1\')
added_thread.start()
print(\"all done\\n\")
```py
预想中输出的结果是否为:
```py
T1 start
T1 finish
all done
```py
但实际却是:
```py
T1 start
all done
T1 finish
```py
## 加入 join() 的结果 
线程任务还未完成便输出`all done`。如果要遵循顺序,可以在启动线程后对它调用`join`:
```py
added_thread.start()
added_thread.join()
print(\"all done\\n\")
```py
使用`join`对控制多个线程的执行顺序非常关键。举个例子,假设我们现在再加一个线程`T2`,`T2`的任务量较小,会比`T1`更快完成:
```py
def T1_job():
    print(\"T1 start\\n\")
    for i in range(10):
        time.sleep(0.1)
    print(\"T1 finish\\n\")

def T2_job():
    print(\"T2 start\\n\")
    print(\"T2 finish\\n\")

thread_1 = threading.Thread(target=T1_job, name=\'T1\')
thread_2 = threading.Thread(target=T2_job, name=\'T2\')
thread_1.start() # 开启T1
thread_2.start() # 开启T2
print(\"all done\\n\")

输出的”一种”结果是:

T1 start
T2 start
T2 finish
all done
T1 finish

现在T1T2都没有join,注意这里说”一种”是因为all done的出现完全取决于两个线程的执行速度, 完全有可能T2 finish出现在all done之后。这种杂乱的执行方式是我们不能忍受的,因此要使用join加以控制。

我们试试在T1启动后,T2启动前加上thread_1.join():

thread_1.start()
thread_1.join() # notice the difference!
thread_2.start()
print(\"all done\\n\")

输出结果:

T1 start
T1 finish
T2 start
all done
T2 finish

可以看到,T2会等待T1结束后才开始运行。

如果我们在T2启动后放上thread_1.join()会怎么样呢?

thread_1.start()
thread_2.start()
thread_1.join() # notice the difference!
print(\"all done\\n\")
``
输出结果:
```py
T1 start
T2 start
T2 finish
T1 finish
all done

T2T1之后启动,并且因为T2任务量小会在T1之前完成;而T1也因为加了joinall done在它完成后才显示。

你也可以添加thread_2.join()进行尝试,但为了规避不必要的麻烦,推荐如下这种1221的V型排布:

thread_1.start() # start T1
thread_2.start() # start T2
thread_2.join() # join for T2
thread_1.join() # join for T1
print(\"all done\\n\")

\"\"\"
T1 start
T2 start
T2 finish
T1 finish
all done
\"\"\"

1.4 储存进程结果 Queue

1.5 GIL 不一定有效率

1.6 线程锁 Lock

收藏 打印