一、多线程的概述
1. 进程与线程
- 进程 :在系统中正在运行的一个 应用程序 ,程序一旦运行就是进程。
- 线程 : 系统分配处理时间资源的基本单元,或者说进程之内独立执行的一个单元试行流。
- 进程 中最少有一个线程。
2.进程的组成
- 内存 : 每个进程的内存是相互独立的。
- 文件/网络句柄 : 它们是所有的进程所共有的。
- 线程
3. 进程与线程—-汇总
- 进程有分配已大部分的内存,而线程只需要分配一部分栈就可以了。
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 进程是资源分配的最小单位,线程是程序执行的最小单位。
- 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
4.多线程
- 使用线程可以把 占据长时间 的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人。
- 程序的运行速度可能加快。
- 在一些 等待的任务 的实现上,线程就比较有用。
5.多线程的执行方式
- 串行 :依次执行所有的线程,上一个线程没有执行完之前,下一个线程不会执行。
- 并行 : 所有线程同时执行。
- 并发 : 所有线程依次 交替 执行。
二、Threading 模块
1. Threading 模块 常用方法
Thread()
: 创建线程,target
为需要执行的函数,args
为执行函数的实参(参数为元组)run()
: 用以表示线程活动的方法。start()
: 启动线程活动。join([time])
: 等待至线程中止。这阻塞调用线程直至线程的 join()
方法被调用中止 - 正常退出或者抛出未处理的异常 - 或者是可选的超时发生。isAlive()
: 返回线程是否活动的。gitName()
: 返回线程名。setName()
: 设置线程名。threading.currentThread()
: 返回当前的线程变量。threading.enumerate()
: 返回一个包含正在运行的线程的 list
。正在运行 指线程启动后、结束前,不包含 启动前和终止后的线程。threading.activeCount()
: 返回正在运行的线程数量,与 len(threading.enumerate())
有相同的结果。
2.多线程案例
普通方法:
1 2 3 4 5 6 7 8 9 10 11 12
| import time
def doing(something): time.sleep(2) print("正在做 >>> :", something)
start_time = time.time() doing("在学习") doing("在加班") end_time = time.time()
print("使用时间:", end_time-start_time)
|
多线程方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import threading import time
def doing(something): print("正在做 >>> :", something) time.sleep(2)
start_time = time.time()
""" target : 需要执行的函数名 args : 执行函数的实参(参数为元组)一个参数时需要加 逗号“,” """ a = threading.Thread(target=doing, args=("在学习",)) b = threading.Thread(target=doing, args=("在加班",))
a.start() b.start()
a.join() b.join() end_time = time.time()
print("使用时间:", end_time-start_time)
|
三、 多线程技术
1. 全局解释器锁(GIL)
在 CPython 中,无论 CPU 有多少核,同时只能执行 一个线程,这是用于 GIL 的存在导致的。
可以把 GIL 看作是执行任务的 “通行证”,并且在一个 Python 进程中, GIL 只有一个。
示例:
某些情况下,多个线程同时操作对一个变量时,可能会导致数据混乱,所以需要 加锁 ,来防止变量依次更改 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import threading import time
money = 1000 lock = threading.Lock()
def test(num): global money lock.acquire()
userMoney = money userMoney += num time.sleep(2) money = userMoney
lock.release()
if __name__ == '__main__': t1 = threading.Thread(target=test, args=(500,)) t2 = threading.Thread(target=test, args=(-200,))
t1.start() t2.start()
t1.join() t2.join()
print(f"余额为:{money}")
|
2.守护进程
在进程想要退出进程的时候,不需要等待自己运行结束,直接退出 即可,
示例:
不论子线程是否执行结束,主线程执行完后直接结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import threading import time
def doing(something): print("正在做 >>> :", something) time.sleep(2) print(something, "结束了")
if __name__ == '__main__': start_time = time.time()
a = threading.Thread(target=doing, args=("在学习",)) b = threading.Thread(target=doing, args=("在加班",))
a.setDaemon(True) b.setDaemon(True)
a.start() b.start()
for one in range(2): print("主线程在运行:", one)
end_time = time.time() print("使用时间:", end_time-start_time)
|
- 如果只有
t1.start()
,没有 t1.join()
和 t1.setDaemon(True)
- 所有线程全部运行完成,这用会出现 主线程结束后,子线程继续运行 , 直到结束。
- 如果只有
t1.start()
、t1.join()
,没有 t1.setDaemon(True)
, - 如果有
t1.start()
、 t1.setDaemon(True)
,没有 t1.join()
- 主线程完成, 所有子线程都 中断。
3. 死锁
在线程间 共享多个资源 的时候,如果两个线程分别占有一部分资源并且 同时等待 对方的资源,就会造成 死锁。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import threading import time
lockA = threading.Lock() lockB = threading.Lock()
def fool(): lockA.acquire() print("请解释什么是死锁") time.sleep(1)
lockB.acquire() print("发 offer") time.sleep(1)
lockA.release() lockB.release()
def user(): lockB.acquire() print("请给我发 offer") time.sleep(1)
lockA.acquire() print("解释了什么是死锁") time.sleep(1)
lockA.release() lockB.release()
t1 = threading.Thread(target=fool) t2 = threading.Thread(target=user)
t1.start() t2.start()
|
4.递归锁
为了支持在同一线程中多次请求同一资源, Python 提供了 “可重入锁” : threading.RLock
。
RLock 内部维护着一个 Lock
和一个 counter
**变量 , counter 记录了 acquire 的次数 , 从而使得资源可以被多次 acquire** 。
递归锁内部维护着一把 锁 和一个 计数器
每次 上锁,计数器 加 ,每次 解锁 ,计数器 减
计数器可以大于零也可以等于零,但不能 小于零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import threading import time
lockR = threading.RLock()
def fool(): lockR.acquire() print("请解释什么是死锁") time.sleep(1)
lockR.acquire() print("发 offer") time.sleep(1)
lockR.release() lockR.release()
def user(): lockR.acquire() print("请给我发 offer") time.sleep(1)
lockR.acquire() print("解释了什么是死锁") time.sleep(1)
lockR.release() lockR.release()
t1 = threading.Thread(target=fool) t2 = threading.Thread(target=user)
t1.start() t2.start()
|