也就是说notify通知唤醒的线程不会从其wait函数返回,并继续执行,而是直到其获得了条件变量中的锁。调用notify的线程应该主动释放锁,因为notify函数不会释放。
那这里就会有一个问题,当前线程修改了其他线程执行的条件,通知其他线程后并主动调用wait释放锁挂起自己,如果其他线程执行的条件均不满足,那所有线程均会阻塞;
下面通过两个线程交替打印字符"A"和“B”来说明条件变量的使用:
share_data, max_len = '#', 6 cond = threading.Condition() def addA(tname): show_start_info(tname) cond.acquire() time.sleep(1) show_acquire_info(tname) global share_data while len(share_data) <= max_len: if share_data[-1] != 'A': share_data += 'A' time.sleep(1) cond.notify() show_add_once_res(tname) else: # show_wait_info(tname) cond.wait() cond.release() show_end_info(tname) def addB(tname): show_start_info(tname) cond.acquire() time.sleep(1) show_acquire_info(tname) global share_data while len(share_data) <= max_len: if share_data[-1] != 'B': share_data += 'B' time.sleep(1) cond.notify() show_add_once_res(tname) else: # show_wait_info(tname) cond.wait() cond.release() show_end_info(tname) if __name__ == "__main__": t1 = threading.Thread(target=addA, args=("Thread 1", )) t2 = threading.Thread(target=addB, args=("Thread 2", )) t1.start() t2.start() t1.join() t2.join() print "share_data:", share_data
结果:
Thread 1 start at: 2019-08-10 17:47:54 Thread 2 start at: 2019-08-10 17:47:54 Thread 1 acquire at: 1565430475.68 Thread 1 add: #A at: 1565430476.68 Thread 2 acquire at: 1565430477.68 Thread 2 add: #AB at: 1565430478.68 Thread 1 add: #ABA at: 1565430479.68 Thread 2 add: #ABAB at: 1565430480.68 Thread 1 add: #ABABA at: 1565430481.68 Thread 2 add: #ABABAB at: 1565430482.68 End Thread 2 with: #ABABAB at: 1565430482.68 End Thread 1 with: #ABABAB at: 1565430482.68 share_data: #ABABAB
View Code结果中可以看出双线程执行的过程以及时间节点。
事件Event
最后再来看一种简单粗暴的线程间同步方式:Event.
该方式的核心就是使用事件控制一个全局变量的状态:True or False。线程执行过程中先判断变量的值,为True就执行,否则调用wait阻塞自己;
当全局变量的状态被set为True时,会唤醒所有调用wait而进入阻塞状态的线程;需要暂停所有线程时,使用clear将全局变量设置为False;
下面使用一个两个玩家掷骰子,一个裁判判断胜负,共三轮的游戏来演示一下事件event的使用;
E = threading.Event() E.clear() res1, res2, cnt, lst = 0, 0, 3, ("player2", 'both', 'player1') def show_round_res(): print ("Stop! judging... %s win!" % lst[cmp(res1, res2) + 1]) def judge(): global cnt while cnt > 0: print 'start game!' E.set() time.sleep(1) E.clear() show_round_res() time.sleep(1) cnt -= 1 print "game over by judge!" def player1(): global res1 while cnt > 0: if E.is_set(): res1 = random.randint(1, 6) print "player1 get %d" % res1 time.sleep(1.5) E.wait() print "player1 quit!" def player2(): global res2 while cnt > 0: if E.is_set(): res2 = random.randint(1, 6) print "player2 get %d" % res2 time.sleep(1.5) E.wait() print "player2 quit!" if __name__ == "__main__": t1 = threading.Thread(target=judge, args=( )) t2 = threading.Thread(target=player1, args=( )) t3 = threading.Thread(target=player2, args=( )) t1.start() t2.start() t3.start() t1.join() E.set()
有一点需要注意的是,线程调用wait时,只有当变量值为False时才会阻塞当前线程,如果全局变量是True,会立即返回;
下面是一种可能的结果:
start game! player1 get 2 player2 get 2 Stop! judging... both win! start game! player2 get 3 player1 get 4 Stop! judging... player1 win! start game! player1 get 1 player2 get 3 Stop! judging... player2 win! game over by judge! player2 quit! player1 quit!
View Code 总结上面是为了解决线程安全问题所采取的一些同步措施。Python为进程同步提供了类似线程的同步措施,例如锁、信号量等。
不同于线程间共享进程资源,进程拥有独立的地址空间,不同进程内存空间是隔离的。
因此对于进程我们通常关注他们之间的通信方式,后续会有文章介绍进程之间的同步。
Linux公社的RSS地址:https://www.linuxidc.com/rssFeed.aspx