活动地址:CSDN21天学习挑战赛
多线程threading 模块
threading 就是多线程相关模块,作用是充分利用cpu的核心数来达到快速执行任务的目的。
1,科普
- 软件:安装在硬盘上的可以运行但没有运行的程序叫软件。
- 进程:正在运行的软件。(杀死进程,结束进程Ctrl+Shift+Delete,小时候玩电脑经常按这个,懂了吧)
- 线程:进程中同时工作的分支,一个进程至少有一个线程,叫做主线程。线程的宿主是进程,线程和进程的区别是操作系统做的。
- 8核CPU:指的是1块CPU有8个相对独立的小CPU组成,物理上只能同时处理8个任务,也就是物理上是8核8线程。
- 多线程:比如一个人左右手都能写字,如果先用右手写一会儿,再用左手写,这样两手交替着写
- 并行:如果两只手同时下笔,左手画圆,右手画方。
2,多线程
- 我们讲线程一般是讲当个进程内的线程,而不是讲多个进程内的线程。换句话说:我们说的线程是程序跑起来,运行在进程里的线程(局限在这一个程序)。
- 多线程比进程更小的执行单位,多线程给人一种多个事件同时发生的感觉
- 一个cpu在同一时间只能执行一个线程,而且在多线程时,因为需要各个线程的来回切换,完成的时间要比单线程还要慢。不过即使这样,多线程也有很多优点,比如可以提前计算机的cpu利用率,而且可以增强用户的体验。可以让用户在同一时间进行多种任务。
- 多线程技术充分使用CPU的核心数一起去执行某项业务,例如:刷新300000张流量卡的最新数据(第三方api),单线程的话就是从头到尾的遍历请求耗时长,开启多线程执行效率会高很多。
- 多线程不是把核心数占满,cpu飙升100%,而是由计算机动态分配,表现出来就是句柄数。
- 我是8核的CPU,最高只能开8个线程吗?
- 不是的,CPU内核数,和进程线程没直接关系,不管开多少线程,最后都是由8个CPU完成任务的。
3,threading里面的方法
- active_count() 返回处于active状态的thread对象
- enumerate()返回所有处于active状态的thread对象列表
- current_thread()返回当前thread对象
- main_thread()返回主线程对象
- get_ident()返回当前线程的线程标识
- stack_size()返回创建线程时使用的栈的大小
4,线程不安全
- 怎么回事呢?就是说多线程在操作同一个变量时,因为线程都想赶紧完成作业去玩耍,就会抢这个变量,导致这个变量的值偏离了预期,甚至出现及其不可思议的现象,就会称作线程不安全,为了使结果符合预计,那么就要引入加锁,解锁,互斥锁的概念。
- 第一个拿到变量的线程,立马锁住,其他线程来抢,发现上锁了,只能待定。
- 第一个线程工作处理完毕后,解锁,等待的线程中只有一个可以抢到这个变量,抢到后立马锁住,其他线程只能等待解锁。
- 直到所有线程运行完毕。
下面代码举例:某工作大厅空间有限,排一队占地方,于是排成2队,男生一队,女生一队,到同一个取号机取号,这个过程会出现2个人同时进入大厅的情况,但是不能同时给相同的号码,这时候就看男生和女生的礼让,看把号给谁了(臆想的哦,实际上是不是,我也不知道)。
代码
#coding=utf-8
import threading
from time import sleep, ctime
# 发号系统(当前号码)
tired = 0
def do_homework(num):
global tired
for i in range(num):
carry_value = tired
print(f'【女士_{i+1}】:是第{carry_value}个进入大厅的')
# 上锁 下面这段就是发号机操作阶段(刷身份证)
tired_lock.acquire()
if tired==0 :
print("...")
print(f'【女士_{i+1}】:是第1个来到发号机并按下发号按钮的,恭喜,第一名。')
tired += 1
thread_obj = threading.current_thread()
print("...")
print(f'【女士_{i+1}】:通过女生队列【{thread_obj.name}】来到发号机,刷身份证取得票号是:{tired}')
# 解锁
tired_lock.release()
def listen_music(num):
global tired
for i in range(num):
carry_value = tired
print(f'【男士_{i+1}】:是第{carry_value}个进入大厅的')
tired_lock.acquire() # 上锁
if tired==0 :
print(f'【男士_{i+1}】:是第1个找到发号机的。')
tired += 1
thread_obj = threading.current_thread()
print("...")
print(f'【男士_{i+1}】:通过男生队列【{thread_obj.name}】来到发号机,刷身份证取得票号是:{tired}')
tired_lock.release() # 解锁
# 创建一个互斥锁
# 默认是未上锁的状态
tired_lock = threading.Lock()
# 测试环境运行
if __name__ == '__main__':
print('---某日9:00,某工作大厅开始营业---:%s'%ctime())
#print('---开始学习前疲劳值---:%d'%tired)
# 除了主线程,继续开2个线程帮忙完成工作
# 为了排队便利,男生一队,女生一队
t1 = threading.Thread(target=do_homework, args=(5,))
# 启动线程
t1.start()
t2 = threading.Thread(target=listen_music, args=(5,))
t2.start()
# 设置while循环,每间隔1秒检查1次
while True:
# 返回所有处于active状态的thread对象列表
count =len(threading.enumerate())
# print(f'当前有{count}个线程在工作')
# 程序停止,则不再循环
if count <= 1:
break
# 主程序等待
sleep(1)
# 主线程会等待所有子线程结束后才结束
print('---营业结束---:%s'%ctime())
print(f"今日总共办理业务数量为:{tired}")
运行结果:
人是没有第0个,所以上面的代码还有不足。。。
提醒:在发布作品前请把不用的内容删掉(活动地址请保留)