中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

python使用threading.Condition交替打印兩個字符的方法

發布時間:2021-03-30 09:49:33 來源:億速云 閱讀:195 作者:小新 欄目:開發技術

這篇文章將為大家詳細講解有關python使用threading.Condition交替打印兩個字符的方法,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

Python中使用threading.Condition交替打印兩個字符的程序。

這個程序涉及到兩個線程的的協調問題,兩個線程為了能夠相互協調運行,必須持有一個共同的狀態,通過這個狀態來維護兩個線程的執行,通過使用threading.Condition對象就能夠完成兩個線程之間的這種協調工作。

threading.Condition默認情況下會通過持有一個ReentrantLock來協調線程之間的工作,所謂可重入鎖,是只一個可以由一個線程遞歸獲取的鎖,此鎖對象會維護當前鎖的所有者(線程)和當前所有者遞歸獲取鎖的次數(本文在邏輯上和可重入鎖沒有任何關系,完全可以用一個普通鎖替代)。

Python文檔中給出的描述是:它是一個與某個鎖相聯系的變量。同時它實現了上下文管理協議。其對象中除了acquire和release方法之外,其它方法的調用的前提是,當前線程必須是這個鎖的所有者。

通過代碼和其中的注釋,能夠非常明白地弄清楚Condition的原理是怎樣的:

import threading
import time
import functools
 
 
def worker(cond, name):
 """worker running in different thread"""
 with cond: # 通過__enter__方法,獲取cond對象中的鎖,默認是一個ReentrantLock對象
  print('...{}-{}-{}'.format(name, threading.current_thread().getName(), cond._is_owned()))
  cond.wait() # 創建一個新的鎖NEWLOCK,調用acquire將NEWLOCK獲取,然后將NEWLOCK放入等待列表中,\
  # 釋放cond._lock鎖(_release_save),最后再次調用acquire讓NEWLOCK阻塞
 print('wait returned in {}'.format(name))
 
 
if __name__ == '__main__':
 condition = threading.Condition()
 t1 = threading.Thread(target=functools.partial(worker, condition, 't1'))
 t2 = threading.Thread(target=functools.partial(worker, condition, 't2'))
 
 t2.start() # 啟動線程2
 t1.start() # 啟動線程1
 
 time.sleep(2)
 with condition:
  condition.notify(1) # 按照FIFO順序(wait調用順序),釋放一個鎖,并將其從等待列表中刪除
 
 time.sleep(2)
 
 with condition:
  condition.notify(1) # 按照FIFO順序(wait調用順序),釋放另一個鎖,并將其從等待隊列中刪除
 
 t1.join() # 主線程等待子線程結束
 t2.join() # 主線程等待子線程結束
 
 print('All done')

其輸出為:

...t2-Thread-2-True
...t1-Thread-1-True
wait returned in t2
wait returned in t1
All done

其中wait方法要求獲取到threading.Condition對象中的鎖(如果沒有提供,默認使用一個可重入鎖),然后自己創建一個新的普通鎖(NEWLOCK),并獲取這個NEWLOCK;之后調用_release_save方法釋放threading.Condition對象中的鎖,讓其它線程能夠獲取到;最后再次調用NEWLOCK上的acquire方法,由于在創建時已經acquire過,所以此線程會阻塞在此。而wait想要繼續執行,必須等待其它線程將產生阻塞的這個NEWLOCK給release掉,當然,這就是notify方法的責任了。

notify方法接收一個數字n,從等待列表中取出相應數量的等待對象(讓wait方法阻塞的鎖對象),調用其release方法,讓對應的wait方法能夠返回。而notify_all方法僅僅就是將n設置為等待列表的總長度而已。

在理解了threading.Condition對象中wait和notify的工作原理之后,我們就可以利用它們來實現兩個線程交替打印字符的功能了:

import threading
import functools
import time
 
 
def print_a(state):
 while True:
  if state.closed:
   print('Close a')
   return
  print('A')
  time.sleep(2)
  state.set_current_is_a(True)
  state.wait_for_b()
 
 
def print_b(state):
 while True:
  if state.closed:
   print('Close b')
   return
  state.wait_for_a()
  print('B')
  time.sleep(2)
  state.set_current_is_a(False)
 
 
if __name__ == '__main__':
 class State(object):
  """state used to coordinate multiple(two here) threads"""
  def __init__(self):
   self.condition = threading.Condition()
   self.current_is_a = False
   self.closed = False
 
  def wait_for_a(self):
   with self.condition:
    while not self.current_is_a:
     self.condition.wait()
 
  def wait_for_b(self):
   with self.condition:
    while self.current_is_a:
     self.condition.wait()
 
  def set_current_is_a(self, flag):
   self.current_is_a = flag
   with self.condition:
    self.condition.notify_all()
 
 
 state = State()
 t1 = threading.Thread(target=functools.partial(print_a, state))
 t2 = threading.Thread(target=functools.partial(print_b, state))
 
 try:
  t1.start()
  t2.start()
 
  t1.join()
  t2.join()
 except KeyboardInterrupt:
  state.closed = True
  print('Closed')

可以看到有兩種類型的任務,一個用于打印字符A,一個用于打印字符B,我們的實現種讓A先于B打印,所以在print_a中,先打印A,再設置當前字符狀態并釋放等待列表中的所有鎖(set_current_is_a),如果沒有這一步,current_is_a將一直是False,wait_for_b能夠返回,而wait_for_a卻永遠不會返回,最終效果就是每隔兩秒就打印一個字符A,而B永遠不會打印。另一個副作用是如果wait_for_a永遠不會返回,那print_b所在線程的關閉邏輯也就無法執行,最終會成為僵尸線程(這里的關閉邏輯只用作示例,生產環境需要更加完善的關閉機制)。

考慮另一種情況,print_a種將set_current_is_a和wait_for_b交換一下位置會怎么樣。從觀察到的輸出我們看到,程序首先輸出了一個字符A,以后,每隔2秒鐘,就會同時輸出A和B,而不是交替輸出。原因在于,由于current_is_a還是False,我們先調用的wait_for_b其會立即返回,之后調用set_current_is_a,將current_is_a設置為True,并釋放所有的阻塞wait的鎖(notify_all),這個過程中沒有阻塞,print_a緊接著進入了下一個打印循環;與此同時,print_b中的wait_for_a也返回了,進入到B的打印循環,故最終我們看到A和B總是一起打印。

可見對于threading.Condition的使用需要多加小心,要注意邏輯上的嚴謹性。

附一個隊列版本:

import threading
import functools
import time
from queue import Queue
 
 
def print_a(q_a, q_b):
 while True:
  char_a = q_a.get()
  if char_a == 'closed':
   return
  print(char_a)
  time.sleep(2)
  q_b.put('B')
 
 
def print_b(q_a, q_b):
 while True:
  char_b = q_b.get()
  if char_b == 'closed':
   return
  print(char_b)
  time.sleep(2)
  q_a.put('A')
 
 
if __name__ == '__main__':
 q_a = Queue()
 q_b = Queue()
 
 t1 = threading.Thread(target=functools.partial(print_a, q_a, q_b))
 t2 = threading.Thread(target=functools.partial(print_b, q_a, q_b))
 
 try:
  t1.start()
  t2.start()
 
  q_a.put('A')
 
  t1.join()
  t2.join()
 except KeyboardInterrupt:
  q_a.put('closed')
  q_b.put('closed')
 
 print('Done')

隊列版本邏輯更清晰,更不容易出錯,實際應用中應該選用隊列。 

附一個協程版本(Python 3.5+):

import time
import asyncio
 
 
async def print_a():
 while True:
  print('a')
  time.sleep(2) # simulate the CPU block time
  await asyncio.sleep(0) # release control to event loop
 
 
async def print_b():
 while True:
  print('b')
  time.sleep(2) # simulate the CPU block time
  await asyncio.sleep(0) # release control to event loop
 
 
async def main():
 await asyncio.wait([print_a(), print_b()])
 
 
if __name__ == '__main__':
 loop = asyncio.get_event_loop()
 loop.run_until_complete(main())

協程的運行需要依附于一個事件循環(select/poll/epoll/kqueue),通過async def將一個函數定義為協程,通過await主動讓渡控制權,通過相互讓渡控制權完成交替打印字符。整個程序運行于一個線程中,這樣就沒有線程間協調的工作,僅僅是控制權的讓渡邏輯。對于IO密集型操作,而沒有明顯的CPU阻塞(計算復雜,以致出現明顯的延時,比如復雜加解密算法)的情況下非常合適。

附一個Java版本:

PrintMain類,用于管理和協調打印A和打印B的兩個線程:

package com.cuttyfox.tests.self.version1;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
 
public class PrintMain {
 private boolean currentIsA = false;
 
 public synchronized void waitingForPrintingA() throws InterruptedException {
  while (this.currentIsA == false) {
   wait();
  }
 }
 
 public synchronized void waitingForPrintingB() throws InterruptedException {
  while (this.currentIsA == true) {
   wait();
  }
 }
 
 public synchronized void setCurrentIsA(boolean flag) {
  this.currentIsA = flag;
  notifyAll();
 }
 
 public static void main(String[] args) throws Exception {
  PrintMain state = new PrintMain();
  ExecutorService executorService = Executors.newCachedThreadPool();
  executorService.execute(new PrintB(state));
  executorService.execute(new PrintA(state));
  executorService.shutdown();
  executorService.awaitTermination(10, TimeUnit.SECONDS);
  System.out.println("Done");
  System.exit(0);
 }
}

打印A的線程(首先打印A):

package com.cuttyfox.tests.self.version1;
 
import java.util.concurrent.TimeUnit;
 
public class PrintA implements Runnable{
 private PrintMain state;
 
 public PrintA(PrintMain state) {
  this.state = state;
 }
 
 public void run() {
  try {
   while (!Thread.interrupted()){
    System.out.println("Print A");
    TimeUnit.SECONDS.sleep(1);
    this.state.setCurrentIsA(true);
    this.state.waitingForPrintingB();
   }
  } catch (InterruptedException e) {
   System.out.println("Exit through Interrupting.");
  }
 
 }
}

打印B的線程:

package com.cuttyfox.tests.self.version1;
 
import java.util.concurrent.TimeUnit;
 
public class PrintB implements Runnable{
 private PrintMain state;
 
 public PrintB(PrintMain state) {
  this.state = state;
 }
 
 public void run() {
  try{
   while (!Thread.interrupted()) {
    this.state.waitingForPrintingA();
    System.out.println("Print B");
    TimeUnit.SECONDS.sleep(1);
    this.state.setCurrentIsA(false);
   }
  } catch (InterruptedException e) {
   System.out.println("Exit through Interrupting.");
  }
 
 }
}

Java對象本身有對象鎖,故這里沒有像Python中那樣需要顯式通過創建一個Condition對象來得到一把鎖。

使用Python實現交替打印abcdef的過程:

import threading
import time
import functools
from collections import deque
 
 LETTERS = [chr(code) for code in range(97, 97+6)]
 LENGTH = len(LETTERS)
 
 
 class State(object):
  def __init__(self):
   self.condition = threading.Condition()
   self.index_value = 0
 
  def set_next_index(self, index):
   with self.condition:
    self.index_value = index
    self.condition.notify_all()
 
  def wait_for(self, index_value):
   with self.condition:
    while not self.index_value == index_value:
     self.condition.wait()
 
 
 def print_letter(state: State, wait_ident: int):
  print('Got: {}!'.format(wait_ident))
  while True:
   state.wait_for(wait_ident)
   time.sleep(2)
   print(LETTERS[state.index_value])
   print('PRINT: {} AND SET NEXT: {}'.format(state.index_value,
              (state.index_value + 1) % LENGTH
              ))
   state.set_next_index((state.index_value + 1) % LENGTH)
 
 
 state = State()
 d = deque()
 d.extend(range(LENGTH))
 d.rotate(1)
 print(d)
 
 threads = []
 for wait_ident in d:
  t = threading.Thread(target=functools.partial(print_letter, state, wait_ident))
  threads.append(t)
 
 for thread in threads:
  thread.start()
 
 for thread in threads:
  thread.join()

關于“python使用threading.Condition交替打印兩個字符的方法”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

噶尔县| 布尔津县| 阳高县| 含山县| 淮安市| 门头沟区| 尖扎县| 和平县| 咸宁市| 西吉县| 望江县| 英吉沙县| 濮阳市| 共和县| 大连市| 余庆县| 英超| 商河县| 恩平市| 克东县| 利辛县| 天水市| 通榆县| 太康县| 临泽县| 定西市| 巫溪县| 呼伦贝尔市| 东丰县| 景谷| 比如县| 长丰县| 鹤岗市| 庆城县| 同仁县| 鄄城县| 沙雅县| 宝山区| 嘉黎县| 高唐县| 安溪县|