Python高级培训第四课(寒假)2022.1.9

多线程

1.信号量

信号量可以理解为多把锁,同时允许多个线程来更改数据。信号量是用来控制线程并发数的Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release()时+1。

计数器不能小于0,当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。

import threading
import time

def run(n, x):
    semaphore.acquire()     # 2.给线程上锁
    print(n)
    time.sleep(x)
    semaphore.release()     # 3.将锁释放            

if __name__ == '__main__':
    semaphore = threading.Semaphore(5)     # 1.先实例化一把锁,传入参数为几则同时运行几个线程
    for i in range(16):
        t = threading.Thread(target=run, args=(i, i))
        t.start()

注:当i=0时,线程耗费0秒就结束了,则同时运行线程1、2、3、4、5。一旦同时运行的线程数不够五个,就会将锁释放,往下进行下一个线程。

2. 条件变量

(1)线程条件变量的相关函数

    acquire() — 线程锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

    release() — 释放锁,注意线程条件变量Condition中的所有相关函数使用必须在acquire() /release() 内部操作;

    wait(timeout) — 线程挂起(阻塞状态),直到收到一个notify通知或者超时才会被唤醒继续运行(超时参数默认不设置,可选填,类型是浮点数,单位是秒)。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError;

    notify(n=1) — 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,缺省参数,默认是通知一个正等待通知的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError,notify()不会主动释放Lock;

    notifyAll() — 如果wait状态线程比较多,notifyAll的作用就是通知所有线程.

(2)线程条件变量原理

    条件变量可以在并行访问共享资源时,保护共享资源,防止出现脏数据。同时条件变量自身提供了wait/notify/notifyAll方法,用于阻塞/通知其他并行线程,可以访问共享资源了。可以这么理解,条件变量提供了一种多线程通信机制,假如线程1需要数据,那么线程1就阻塞等待,这时线程2就去制造数据,线程2制造好数据后,通知线程1可以去取数据了,然后线程1去获取数据。

(3)线程条件变量的使用

import threading
import time

def run(x):
    con.acquire()     #线程上锁
    print(f'线程{x}')
    con.notify()     #唤醒正在等待的线程
    print(f'线程{x}挂起')
    con.wait()     #等待对方回应消息,使用wait阻塞本线程,等待对方通过notify唤醒本线程
    time.sleep(1)
    print(f'线程{x}再次启动')
    con.notify()     #唤醒对方
    con.release()     #线程释放锁

if __name__ == '__main__':
    con = threading.Condition()     #创建条件变量
    for i in range(10):
        t = threading.Thread(target=run, args=(i, ))
        t.start()

使用两个线程,更加清晰的展现条件变量整个程序执行的顺序

(在没有使用条件变量的情况下,两个线程应该时同时启动)

import threading
import time

def run1(x):
    con.acquire()     # 2.线程上锁,则先只运行线程1
    print(f'线程{x}')
    con.notify()     # 3.唤醒线程2(发出信号,通知线程2可以开始运行了,但并不影响线程1的运行)
    print(f'线程{x}挂起')
    con.wait()     # 5.线程1停止
    time.sleep(1)     # 7.线程1被唤醒,执行语句(暂停一秒)
    print(f'线程{x}再次启动')
    con.notify()     # 9.唤醒线程2
    con.release()

def run2(x):
    con.acquire()
    print(f'线程{x}')
    con.notify()     # 6.唤醒线程1,但不影响线程2自身的运行
    print(f'线程{x}挂起')
    con.wait()     # 8.线程2停止
    time.sleep(1)     # 10.线程2被唤醒,执行语句(暂停一秒)
    print(f'线程{x}再次启动')
    con.notify()     # 11.若还有其他线程,则唤醒下一个线程
    con.release()

if __name__ == '__main__':
    con = threading.Condition()     #创建条件变量
    t1 = threading.Thread(target=run1, args=(1, ))
    t1.start()     # 1.线程1开始运行
    t2 = threading.Thread(target=run2, args=(2, ))
    t2.start()     # 4.线程2开始运行

 3.事件

    事件,就是多线程之间的通信。事件对象管理一个内部标志,通过set()方法将其设置为True,并使用clear()方法将其设置为Falsewait()方法阻塞,直到标志为True。该标志初始为False

    线程事件的相关函数:

is_set()
当且仅当内部标志为True时返回True

set()
将内部标志设置为True。所有等待它成为True的线程都被唤醒。当标志保持在True的状态时,线程调用wait()是不会阻塞的。

clear()
将内部标志重置为False。随后,调用wait()的线程将阻塞,直到另一个线程调用set()将内部标志重新设置为True

wait(timeout=None)
阻塞直到内部标志为真。如果内部标志在wait()方法调用时为True,则立即返回。否则,则阻塞,直到另一个线程调用set()将标志设置为True,或发生超时。
该方法总是返回True,除非设置了timeout并发生超时。

import threading
import time

def car():
    while True:     #检测事件
        if event.is_set():     #若检测到事件
            print("小车行驶")
        else:                  #若未检测到事件
            print("小车停止")
            event.wait()

def set_event():
    while True:
        event.set()     #设置事件
        time.sleep(1)
        event.clear()      #清除事件
        time.sleep(3)

if __name__ == '__main__':
    event = threading.Event()     #创建事件
    car1 = threading.Thread(target=car)
    car1.start()
    set_e = threading.Thread(target=set_event)
    set_e.start()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值