我与Python的偶遇(第十一篇)


活动地址: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,线程不安全

  • 怎么回事呢?就是说多线程在操作同一个变量时,因为线程都想赶紧完成作业去玩耍,就会抢这个变量,导致这个变量的值偏离了预期,甚至出现及其不可思议的现象,就会称作线程不安全,为了使结果符合预计,那么就要引入加锁,解锁,互斥锁的概念。
    1. 第一个拿到变量的线程,立马锁住,其他线程来抢,发现上锁了,只能待定。
    2. 第一个线程工作处理完毕后,解锁,等待的线程中只有一个可以抢到这个变量,抢到后立马锁住,其他线程只能等待解锁。
    3. 直到所有线程运行完毕。

下面代码举例:某工作大厅空间有限,排一队占地方,于是排成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个,所以上面的代码还有不足。。。

提醒:在发布作品前请把不用的内容删掉(活动地址请保留)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值