4.多线程编程技术
大约 5 分钟学习笔记测试开发测试工具开发
一、多线程的概述
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.多线程案例
普通方法:
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)多线程方法:
import threading
import time
def doing(something):
    print("正在做 >>> :", something)
    time.sleep(2)
start_time = time.time()
# 1.创建线程
"""
target : 需要执行的函数名
args : 执行函数的实参(参数为元组)一个参数时需要加 逗号“,”
"""
a = threading.Thread(target=doing, args=("在学习",))
b = threading.Thread(target=doing, args=("在加班",))
# 2.启动子线程
a.start()
b.start()
# 3.阻塞主线程,直到子线程全部结束
a.join()
b.join()
end_time = time.time()
print("使用时间:", end_time-start_time)三、 多线程技术
1. 全局解释器锁(GIL)
在 CPython 中,无论 CPU 有多少核,同时只能执行 一个线程,这是用于 GIL 的存在导致的。
可以把 GIL 看作是执行任务的 “通行证”,并且在一个 Python 进程中, GIL 只有一个。
示例:
某些情况下,多个线程同时操作对一个变量时,可能会导致数据混乱,所以需要 加锁 ,来防止变量依次更改 。
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.守护进程
在进程想要退出进程的时候,不需要等待自己运行结束,直接退出 即可,
t1.setDaemon(True)示例:
不论子线程是否执行结束,主线程执行完后直接结束。
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)
    # 2.启动子线程
    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. 死锁
在线程间 共享多个资源 的时候,如果两个线程分别占有一部分资源并且 同时等待 对方的资源,就会造成 死锁。
示例:
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** 。
提示
递归锁内部维护着一把 锁 和一个 计数器 每次 上锁,计数器 加 ,每次 解锁 ,计数器 减 计数器可以大于零也可以等于零,但不能 小于零
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()