前面介绍了Python并发编程中,可以使用互斥锁或者可重入锁来实现多线程之间的同步。但是,直接使用锁的同步,主要是对临界区的简单保护,确保任意时刻只有一个线程进入临界区操作临界资源。
由于多线程的调度涉及到时间片轮转、GIL等,执行的顺序是无法精确控制的。
如果想要实现更加精确的线程执行顺序的控制,类似于多线程交替执行的效果,可以考虑使用Condition。
本文的主要内容有:
1、需求场景:排排坐,分果果
2、使用锁的效果
3、使用Condition真正实现需求
4、Condition的实现原理及使用注意事项
需求场景:排排坐,分果果假设我们有这样一个需求场景:
1、总共有10部质量参差不齐的小米15
2、张三和李四平分这10部手机
3、为了尽量公平,俩人轮流进行挑选,每人每次选一部,然后由下一个人来选,直至所有的手机瓜分完成。
接下来我们通过锁和Condition来分别尝试实现该需求场景。
使用锁的效果直接看代码:
对代码做一个简单说明:
1、通过继承Thread类实现自定义选手机的线程,初始化方法中,传入的参数:name表示姓名,count表示每人可以选的个数,lock就是尝试同步步调的锁。
2、通过重载run()方法,实现选手机的业务逻辑,通过一个延时模拟效果。
执行结果如下:
从执行结果中,我们可以看到明显的不公平出现了。实际上,如果我们不进行延时的设置,会更加不公平,可能的结果是张三调了5个之后,李四才开始选。
显然,通过锁试图实现两个线程实现交替执行的线程同步,是无法达到预期的效果的。
使用Condition真正实现需求接下来,通过Condition来尝试实现该需求。
直接看代码以及执行效果,之后再来解释Condition的使用
程序的执行效果:
可以看到,真正实现了“排排坐,分果果,我一个,你一个”的效果。
Condition的实现原理及使用注意事项Condition的实现原理
从上面的文档中可以看出Condition的实现原理:
1、每个Condition对象包含一个可重入锁(RLock),也可以在创建时传递一个锁对象。用于控制对共享资源的访问。这个内部锁保证了条件变量的操作是原子的。
2、Condition对象实现了__enter__()和__exit__()这两个上下文管理的魔术方法,所以,可以使用with语法糖来使用Condition。
3、Condition对象中维护了一个等待队列,用于记录当前等待该条件的线程。当条件满足时,可以通过notify()方法或者notify_all()方法来唤醒等待的线程。
Condition对象最核心的方法有:
1、wait()方法:将调用wait()方法的线程加入到等待该条件的等待队列中。
2、notify()方法:从等待队列中唤醒一个等待的线程。
3、notify_all()方法:唤醒等待队列中的所有线程。
使用注意事项
Condition可以实现更加精准的线程同步机制,但是,在使用过程中,需要特别注意一些关键点,从而确保程序能够正确、安全地执行。
1、Condition通常用来保护共享资源,当要访问共享资源时需要获取Condition内部的锁,以避免数据竞争和资源争用。所以在访问或修改共享资源之前,务必先获取锁。
2、为了确保锁的获取和释放能够正确执行,应当尽量使用with语句来管理,这样可以方便地在代码块退出时实现锁资源的自动释放,即便有异常发生。
3、多个线程之间的复杂交互,有可能导致死锁。所以,在设计时应当尽量小心,确保所有需要获取的锁按固定顺序进行获取和释放,避免交叉请求多个锁的情况。
4、Condition.wait()方法可以接收一个timeout参数,用于表示超时自动退出等待的场景。处理超时情况时,必须清晰定义超时后的操作逻辑。
总结本文简单介绍了Condition的内部设计原理、Condition的核心方法,以及在使用Condition实现多线程的复杂同步需求时的注意事项。
以上,就是本文的全部内容了,希望对您有所帮助!