Python高级培训4——多线程


Semaphore(信号量)

信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。
也可以简单的理解为,信号量是多把锁,同时允许多个线程来更改数据,而 互斥锁 同时只允许一个 线程 更改数据。

Python信号量与互斥锁的关系

信号量的一个特殊用法是互斥量。互斥量是初始值为 1 的信号量,可以实现数据、资源的互斥访问。

"""
互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
"""
import threading
sem = threading.Semaphore(3)
sem.acquire()
sem.release()

首先,我们需要使用 threading.Semaphore 创建一个信号量的实例,创建实例时,需要指定一个 value 参数 大小,表示内部维护的计数器的大小,默认为 1。
接着,在我们对临界资源进行访问的时候,调用 acquire(),此时内置计数器 -1,直到为 0 的时候就阻塞。资源调用完毕后调用 release(),内置计数器 +1,并让某个线程的 acquire() 从阻塞变为不阻塞。

条件变量

Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。
线程首先acquire一个条件变量,然后判断一些条件。

  • 如果条件不满足则wait;
  • 如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。

Condition():

  • acquire(): 线程锁
  • release(): 释放锁
  • wait(timeout): 线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。
  • notify(n=1): 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。
  • notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程
"""
前言
当小伙伴a在往火锅里面添加鱼丸,这个就是生产者行为;另外一个小伙伴b在吃掉鱼丸就是消费者行为。当火锅里面鱼丸达到一定数量加满后b才能吃,这就是一种条件判断了。
这就是本篇要讲的Condition(条件变量)

Condition
Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。
可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。

"""
# coding=utf-8
import threading
import time

con = threading.Condition()

num = 0


# 生产者
class Producer(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        # 锁定线程
        global num
        con.acquire()  # 线程锁
        while True:
            print("开始添加!!!")
            num += 1
            print("火锅里面鱼丸个数:%s" % str(num))
            time.sleep(1)
            if num >= 5:
                print("火锅里面里面鱼丸数量已经到达5个,无法添加了!")
                # 唤醒等待的线程
                con.notify()  # 唤醒小伙伴开吃啦
                # 通知其他线程,那些挂起的线程接到这个通知之后会开始运行

                # 等待通知
                con.wait()  # 线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行
        # 释放锁
        con.release()


# 消费者
class Consumers(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        con.acquire()  # 线程锁
        global num
        while True:
            print("开始吃啦!!!")
            num -= 1
            print("火锅里面剩余鱼丸数量:%s" % str(num))
            time.sleep(2)
            if num <= 0:
                print("锅底没货了,赶紧加鱼丸吧!")
                con.notify()  # 唤醒其它线程
                # 等待通知
                con.wait()
        con.release()


p = Producer()
c = Consumers()
p.start()
c.start()

事件

前言

小伙伴a,b,c围着吃火锅,当菜上齐了,请客的主人说:开吃!,于是小伙伴一起动筷子,这种场景如何实现

Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么event.wait 方法时便不再阻塞。

Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

Event()

  • set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
  • clear(): 将标志设为False。
  • wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
  • isSet(): 获取内置标志状态,返回True或False。
# coding:utf-8
"""
前言
小伙伴a,b,c围着吃火锅,当菜上齐了,请客的主人说:开吃!,于是小伙伴一起动筷子,这种场景如何实现
"""
import threading
import time

event = threading.Event()


def chihuoguo(name):
    # 等待事件,进入等待阻塞状态
    print('%s 已经启动' % threading.currentThread().getName())
    print('小伙伴 %s 已经进入就餐状态!' % name)
    time.sleep(1)
    event.wait()
    # 收到事件后进入运行状态
    print('%s 收到通知了.' % threading.currentThread().getName())
    print('小伙伴 %s 开始吃咯!' % name)


# 设置线程组
threads = []

# 创建新线程
thread1 = threading.Thread(target=chihuoguo, args=("a",))
thread2 = threading.Thread(target=chihuoguo, args=("b",))

# 添加到线程组
threads.append(thread1)
threads.append(thread2)

# 开启线程
for thread in threads:
    thread.start()

time.sleep(0.1)
# 发送事件通知
print('主线程通知小伙伴开吃咯!')
event.set()

作业

使用类继承的方式,实现信号量、事件功能操作。具体案例:第一个
线程中获取当前时间,判断当前时间3秒之后,触发“事件” 对象。在另
一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打
印。

import threading
import time


class Task1(threading.Thread):
    def run(self):
        while True:
            print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))  # 输出当前时间
            time.sleep(3)  # 等待3S
            event.set()  # 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
            time.sleep(3)


class Task2(threading.Thread):
    def run(self):
        while True:
            if event.is_set():  # 检测到事件后,触发考试结束
                print("考试结束")
                event.clear()
                event.wait()
            else:  # 没有检测到事件,正在考试中
                print("考试中")


event = threading.Event()
t1 = Task1()  # 实例化对象
t2 = Task2()  # 实例化对象
t1.start()  # 用于启动线程的活动。start()方法在内部调用run()方法,然后执行目标方法。
t2.start()  # 用于启动线程的活动。start()方法在内部调用run()方法,然后执行目标方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值