zoukankan      html  css  js  c++  java
  • python进阶(四、并发编程:线程)

    2.并发编程
    2.5 线程
    2.5.1 线程概念
    1)线程是计算机中能够被操作系统调度(给CPU执行)的最小单位。
    同一个进程中的多个线程,可以同时被不同的CPU执行
    进程之间数据隔离,线程之间数据共享,线程数据不安全,线程开启关闭切换时间开销小。
    一般情况下我们开启的进程数不会超过CPU个数的2倍,线程可以开很多50、100

    2)CPython的gc(垃圾回收)机制:引用计数+分代回收
    为了确保gc机制中引用计数的准确性,CPython使用全局解释器锁GIL。

    3)全局解释器锁GIL(Global Interpreter Lock)导致了一个进程中的多个线程同一时刻只能有一个线程真正被CPU执行。GIL负责各个线程轮转执行

    4)提高程序运行速度,主要考虑节省I/O操作时间,而不是CPU计算时间,因为CPU的计算速度非常快。大部分情况下,我们没有办法把一条进程中所有的I/O操作都规避掉。多线程主要是为了节省I/O操作时间。
    注意:JPython不用gc机制, 可以在一个进程中使用多核。

    2.5.2 threading:线程模块
    1)多线程并发

    2)join():产生同步阻塞

    注意:进程可以使用terminate()方法强制关闭,线程不可以从外部关闭。线程只能执行完成所有代码后自己关闭。

    3)ident查看线程id

    4)current_thread():获取当前线程对象

    5)current_thread().ident:线程id

    6)enumerate()获取线程对象列表:存储了所有活着的线程对象,包括主线程。

    导入enumerate方法后,会和内置函数enumerate重名。导致内置函数不能使用
    处理方法:
    (1)导入时使用as起别名
    (2)导入threading,使用threading.enumerate()

    7)active_count:存储了所有活着线程的数量。

    2.5.3 面向对象启动线程

    2.5.4 线程之间的数据是共享的

    2.5.5 守护线程
    主线程会等子线程结束之后才结束。因为主线程结束进程就会结束,进程结束进程的资源就会被回收,进程内的线程无法运行。
    子进程死循环,会一直执行:

    子进是守护进程,程序很快结束:

    守护线程在主线程代码结束后,会继续守护其它子线程,所有子线程结束后,守护线程才会结束。

    主进程代码结束后,守护进程不会守护其它子进程。
    主线程代码结束后,守护线程会守护其它子线程
    工作过程:
    其它子线程结束-->主线程结束-->主进程结束-->进程资源被回收-->守护线程也被回收

    2.5.6 线程锁:互斥锁(重要)
    1)执行加减操作时数据不安全

    CPU指令执行过程:
    dis函数将python代码编译成cpu指令

    如果a加载后但存储前,被GIL锁轮转;再次轮转回来时,会把计算结果直接保存,导致数据错误。
    数据共享并且多线程异步执行以下操作时,数据不安全:
    +=、-=、*=、/=、=、while、if
    多个进程执行if判断时存在数据,但执行pop时,数据可能已被其它进程弹空了,导致pop操作报错。while操作与if类似,所以while和if也是不安全的。

    2)执行append时数据是安全的


    执行append或pop时,只是添加或弹出一个数据。如果在执行中被GIL轮转,转回时继续添加或弹出即可,不会破坏数据。
    append pop数据安全,列表或字典中的方法去操作全局变量的时候,数据是安全的。另外Queue和logging也是数据安全的

    3)添加线程锁,保护数据

    增加线程锁后,如果a加载后但存储前,被GIL锁轮转。由于其它线程没有钥匙被阻塞,只能轮转回当前线程继续操作,保证了数据的安全。

    if判断加锁

    4)单例模式加锁

    在类中增加阻塞,由于if语句在线程并发中的不安全性,导致单例模型失效

    给单例模式加锁

    小贴士:多线程同时操作全局变量或类里操作静态变量,会出现数据不安全

    2.5.7 线程锁:递归锁

    递归锁可以多次acquire(拿钥匙),多次release(还钥匙)
    1)互斥锁无法多次拿钥匙

    2)递归锁可以多次拿钥匙

    拿几次钥匙,放几次钥匙;代码可以顺利执行

    注意:递归锁的效率比互斥锁低

    3)嵌套使用递归锁
    在fun_1内调用fun_2时,如果使用互斥锁程序会阻塞,使用递归锁可以正常运行

    2.5.8 死锁现象
    多把锁(包括递归锁和互斥锁),在多个线程中交叉使用,容易出现死锁现象




    快速解决办法:将所有锁都统一成一把递归锁,但这样修改会降低代码的运行效率。
    noodle_lock = fork_lock = RLock()

    2.5.9 queue:队列
    队列是线程之间数据安全的容器,先进先出。
    原理:加锁 + 链表
    实现先进先出,利用列表处理效率低
    队列分类: Queue先进先出、LifoQueue 后进先出、PriorityQueue 优先级队列
    每种队列都有4种方法:put、get、put_nowait、get_nowait,各种队列的用法相同
    1)Queue先进先出
    (1)get、put

    (2)put_nowait:会导致数据丢失,一般不使用

    队列数据装满后,使用put方法继续存储数据,程序不会报错,当程序会阻塞无法继续执行。使用put_nowait程序会报错,该错误是queue模块内部的错误,可以使用try方法捕获错误

    (3)get_nowait:
    队列数据取空后,使用get方法继续取数据,程序不会报错,当程序会阻塞无法继续执行。使用get_nowait程序会报错,该错误是queue模块内部的错误,可以使用try方法捕获错误

    2)LifoQueue:last in first out 后进先出:栈

    3)PriorityQueue:优先级队列(VIP)
    按ASCII值进行排序

  • 相关阅读:
    Java8新特性Stream详细教程
    自定义注解!绝对是程序员装逼的利器!!
    如何处理重复请求/并发请求的
    C#字符处理
    mysql 索引
    mysql事件【定时器】
    JS日期,金钱处理
    Controller中使用@Value无法获取属性值
    druid连接池的配置
    mybatiste报错java.lang.ClassCastException
  • 原文地址:https://www.cnblogs.com/bdzxh/p/14081125.html
Copyright © 2011-2022 走看看