2013-11-14 45 views
0

我想寫使用,我希望兩個線程,線程1和線程之間交替程序兩個線程之間交替。棘手的部分是線程應該開始執行,首先必須是線程1。 這是代碼我到目前爲止:多線程 - 使用條件和事件在Python

Class Client: 
#member variables 
def sendFile(self,cv1,lock1): 

     sent=0; 
     while (i<self.size): 
      message = self.data[i:1024+i] 
      cv1.acquire() 
      BadNet.transmit(self.clientSocket,message,self.serverIP,self.serverPort) 
      cv1.notify() 
      cv1.release() 

      i = i+1024 
      sent+=1 
      lock1.wait() 

     print "File sent successfully !" 
     self.clientSocket.close() 

    def receiveAck(self,cv1,lock2): 
     i=0 
     while (1): 
      lock1.clear() 
      cv1.acquire() 
      cv1.wait() 
      print "\nentered ack !\n" 
      self.ack, serverAddress = self.clientSocket.recvfrom(self.buf) 

      cv1.release() 
      lock1.set() 


if __name__ == "__main__": 
    lock1 = Event() 
    cv1 = Condition() 
    cv2= Condition() 
    client = Client(); 
    client.readFile(); 

    thread1 = Thread(target = client.sendFile, args=[cv1,lock1]) 
    thread2 = Thread(target = client.receiveAck, args=[cv1,lock1]) 

    thread1.start() 
    thread2.start() 
    thread1.join() 
    thread2.join() 

我目前面臨的問題是,最初的程序執行兩個線程之間交替(由在控制檯上輸出確認,但迭代任意數目後(通常介於20和80)的程序只是掛起並且不執行進一步的迭代。

+0

我的第一個問題是,你爲什麼要這樣做?如果他們只是明確地交換控制權,那麼有兩個線程有​​什麼意義呢?您可以使用一個生成器更輕鬆地做到這一點? – abarnert

+0

我只是爲了理解目的簡化了我的程序。實際發生的事情是,thread1使用UDP套接字發送數據包,而thread2使用相同的套接字來接收該數據包的確認。除非已收到對前一個數據包的確認,否則Thread1不應繼續進行。 我希望你明白我的意思,但即使你不這樣做,我也會要求你忽略我在兩個主題之間交替的目的,並且善意地幫助我解決我面臨的問題。 –

+0

聲音相當順序...爲什麼使用線程而不是簡單地使用阻塞IO?阻塞,直到你得到你的ACK,然後繼續循環下一個數據包,爲什麼線程? – immortal

回答

1

至少有兩個問題您同步。

首先,你用錯了cv1。您的接收線程具有循環,每次檢查條件並致電wait。否則,你只是使用cv作爲破壞事件+鎖組合。你沒有這樣的循環。更重要的是,你甚至沒有條件等待。

其次,你用錯了lock1。您的接收線程設置事件,然後立即清除它。但是不能保證發送線程已經等待了。 (從以前的問題,這場比賽使得這個更大的問題,但它仍然是一個問題,即使你解決這個問題。)在多核機器,它會通常及時趕到,但「通常」更糟糕比從來沒有在線程編程。因此,在接收線程已經完成清除後,最終發送線程會等待,因此它將永遠等待。與此同時,接收線程將等待由發送線程通知,這將永遠不會發生。所以你陷入僵局。

以供將來參考,之前每阻塞操作後加入print報表,尤其是同步操作,就會使這個有很多調試:你會看到接收線程的最後一條消息是「接收等待CV1」,而發送線程的最後一條消息是「發送等待鎖1」,並且顯而易見的是死鎖的位置。


,無論如何,我不知道它甚至意味着「修理」沒有條件,或者你想爲CV使用事件簡歷,所以不是我將介紹如何用兩個cvs寫一些合理的東西。在這種情況下,我們不妨使用一個標誌,我們將來回翻轉作爲兩個CVS的條件。

雖然我在這裏,但我會解決一些其他問題,使您的代碼無法測試(例如,i從不初始化),幷包含調試信息以及必須填寫的內容這是一個完整的例子,但除此之外,我會盡量保留你的結構和不相關的問題(如Client是一箇舊式類)。

class Client: 
    def __init__(self): 
     self.clientSocket = socket(AF_INET, SOCK_DGRAM) 
     self.serverIP = '127.0.0.1' 
     self.serverPort = 11111 
     self.buf = 4 
     self.waitack = False 

    def readFile(self): 
     self.data = ', '.join(map(str, range(100000))) 
     self.size = len(self.data) 

    #member variables 
    def sendFile(self,cv1,lock1): 
     i = 0 
     sent=0 
     while (i<self.size): 
      message = self.data[i:1024+i] 
      print "s cv1 acquire" 
      with cv1: 
       print "s sendto" 
       self.clientSocket.sendto(message, (self.serverIP, self.serverPort)) 
       self.waitack = True 
       print "s cv1 notify" 
       cv1.notify() 

      i = i+1024 
      sent+=1 

      print "s cv2 acquire" 
      with cv2: 
       print "s cv2 wait" 
       while self.waitack: 
        cv2.wait() 

     print "File sent successfully !" 
     self.clientSocket.close() 

    def receiveAck(self,cv1,lock2): 
     i=0 
     while (1): 
      print "r cv1 acquire" 
      with cv1: 
       while not self.waitack: 
        print "r cv1 wait" 
        cv1.wait() 
      print "r recvfrom" 
      self.ack, serverAddress = self.clientSocket.recvfrom(self.buf) 
      i += 1 
      print self.ack, i    

      print "r cv2 acquire" 
      with cv2: 
       self.waitack = False 
       print "r cv2 notify" 
       cv2.notify() 

下面是它在測試服務器:

from itertools import * 
from socket import * 

s = socket(AF_INET, SOCK_DGRAM) 
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 
s.bind(('127.0.0.1', 11111)) 

for i in count(): 
    data, addr = s.recvfrom(1024) 
    print(i) 
    s.sendto('ack\n', addr) 

啓動服務器,啓動客戶端,服務器將數到672,客戶端會數到673(因爲你的代碼計數基於1的)帶有673對平衡消息和「文件發送成功!」最後。 (當然,客戶端將隨即掛斷永遠因爲receiveAck沒有辦法完成,服務器,因爲我寫的是一個無限循環)。

+0

非常感謝您的回答。它完美的工作,因爲我打算用我的原始代碼。我很抱歉沒有提供完整的功能代碼,下次請小心。至於使用線程執行相當連續的任務的問題,這實際上是我正在處理的這項任務的一個要求! –