zoukankan      html  css  js  c++  java
  • Python中的线程

    线程同步

    概念

    线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作.

    临界区(Critical Section) 互斥量(Mutex) 信号量(Semaphore) 时间(Event)

    Event事件

    Event事件,是线程间通信机制中最简单的实现,使用一个内部标记的flag,通过flag的True或False的变化来进行操作.

    名称 含义
    set() 标记设置为True
    clear() 标记设置为False
    is_set() 标记是否为True
    wait(timeout=None) 设置等标记为True的时长,None为无限等待.等到返回True,未等到超时返回False,不设置默认无限等

    总结

    使用同一个Event对象的标记flag

    谁wait就是等到flag变为true,或等到超时返回False,不限制等待个数

    wait的使用

    Event的wait优于time.sleep,它会更快的切换到其他进程,提高并发效率.

    Lock(互斥锁,互斥量)

    锁,凡是存在共享资源争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源.

    一旦线程获得锁,其他试图获取锁的线程将被阻塞

    名称 含义
    acquire(blocking=True,timeout=-1) 默认阻塞,阻塞可以设置超时时间.非阻塞时,timeout禁止设置.成功获取锁,返回True,否则返回False
    release() 释放锁,可以从任何线程调用释放.已上锁的锁,会被重置为unlocked未上锁的锁上调用,抛RuntimeError异常
    加锁,解锁

    一般来说,有加锁就需要解锁,一旦加锁后解锁前,还要执行一些代码,就可能抛异常,锁无法释放,但是当前线程就可能因为这个异常被终止,产生死锁.一般为逻辑问题.

    常用语句:
    1. 使用try,,,finally语句保证锁的释放
    2. with上下文管理,锁对象支持上下文管理
    注意事项:
    • 少用.使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行
    • 加锁时间越短越好,不需要就立即释放锁
    • 一定要避免死锁

    递归锁RLock(可重入锁)

    递归锁,是线程相关锁,个人认为,锁的是一个线程

    线程A可获得重复锁,并可多次成功获取,不会阻塞.最后要在线程A中做和acquire次数相同的release.

    当锁未释放完,其他线程获取锁会被阻塞,知道当前持有锁的线程释放完锁

    Condition

    构造方法:Condition(lock= None),可以掺入一个Lock或者RLock锁对象,默认RLock

    名称 含义
    acquire(*args) 获取锁
    wait() 等待或超时
    notify(n= 1) 唤醒之多指定数目个数的等待线程,没有等待的线程就没有任何操作
    notify_all() 唤醒所有等待的线程
    总结

    Condition用于生成者消费者模型中,解决生产者消费者速度匹配的问题.

    采用通知机制,效率极高

    使用方法:

    使用Condition,必须先acquire,用完了要release,默认使用RLock.最好的方式是使用with上下文

    消费者wait,等待通知

    生产者生产好消息,对消费者发送通知,可以使用notify或者notify_all方法.

    Barrier栅栏(路闸)

    wait超时

    semaphore信号量

    和Lock很想,信号量对象内部维护一个倒计数器,每一次acquire都会减1,当acquire方法发现技术为0就阻塞请求的线程,知道其他线程对信号量release后,计数大于0,恢复阻塞的线程.

    名称 含义
    Semaphore(value=1) 构造方法.value小于0,抛ValueError异常
    acquire() 获取信号量,计数器减1,成功返回True
    release() 释放信号量,计数器加1

    计数器永远不会低于0,因为acquire的时候,发现是0,都会被阻塞.

    from threading import Semaphore,Lock
    from threading import current_thread
    from threading import Thread
    import time
    
    sm = Semaphore(5)#5个马桶
    def task():
        sm.acquire()
        print(f'{current_thread().name} work')
        time.sleep(1)
        sm.release()
        
    for line in range(20):
        t.Thread(target=task)
        t.start
    
    信号量和锁

    锁,值允许同一个时间一个线程独占资源,它是特殊的信号量,即信号量计数器初值为1.

    信号量,可以多个线程访问共享资源,但这个共享资源数量有限.

    数据结构和GIL

    Queue

    标准库queue模块,提供

    FIFO的队列:先进先出

    LIFO的队列:后进先出

    优先队列:根据参数内,数字的大小进行分级,数值越小,优先级越高(字符串会有自己的算法,不建议使用)

    Queue是线程安全的,适用于多线程间安全的交换数据.内部使用了Lock,Condition

    import queue
    q = queue.Queue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get()) #1
    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get()) # 3
    

    GIL全局解释器锁(Global Interpreter Lock)

    CPython在解释器进程级别有把锁.叫GIL全局解释器锁.

    GIL保证CPython进程中,只有一个线程执行字节码,甚至多核CPU情况下,也会当一个进程A中的线程执行时,阻塞其他CPU上A进程的线程.相当于,CPython中永远没有真正微观实际意义上的多线程.

    CPython中,IO密集型,使用多线程;CPU密集型,使用多继承,绕开GIL

    Ruby也有GIL,如果高并发的话,用erlang或者go

    ​ Python中绝大多数内置数据结构读写都是原子操作.(即不会被优先级更高的线程打断,线程安全)

    ​ 由于GIL存在,Python的内置数据类型在多线程编程的时候就变程了安全的了,但是实际上他们本身不是线程安全类型.

    原子操作:原子操作,就是不能被更高等级中断抢夺优先的操作。由于操作系统大部分时间处于开中断状态,所以,一个程序在执行的时候可能被优先级更高的线程中断。而有些操作是不能被中断的,不然会出现无法还原的后果,这时候,这些操作就需要原子操作。就是不能被中断的操作。

  • 相关阅读:
    Java基础:基本类型
    完全干净的卸载VS2013
    git本地仓库首次push到远程仓库出现错误 ! [rejected] master -> master (fetch first)
    运行VS出现warning C4996错误的解决办法
    xbmc-android的编译
    linux执行sh,出现/bin/sh^M: bad interpreter: No such file or directory
    Ubuntu配置android-vlc编译环境(2015-11-05)
    a80修改默认4k输出,官方代码锁死了
    ubuntu12.04平台下a80编译环境搭建
    编译java代码出现 错误: 需要class, interface或enum 提示
  • 原文地址:https://www.cnblogs.com/agsol/p/11721910.html
Copyright © 2011-2022 走看看