0

thread1 と thread2 の 2 つのスレッドを交互に使用するプログラムを作成しようとしています。トリッキーな部分は、最初に実行を開始するスレッドがスレッド 1 でなければならないことです。これは私がこれまでに持っているコードです:

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()

私が現在直面している問題は、最初にプログラムが 2 つのスレッド間で交互に実行されることです (コンソールの出力で確認されます。ただし、任意の回数 (通常は 20 から 80 の間) の反復の後、プログラムはハングアップし、それ以上の反復は実行されません。

4

1 に答える 1

1

同期には少なくとも 2 つの問題があります。

まず、使い方がcv1間違っています。受信スレッドはその cv をループし、条件をチェックしてwait毎回呼び出す必要があります。それ以外の場合は、壊れたイベント + ロックの組み合わせとして cv を使用しているだけです。あなたはそのようなループを持っていません。さらに重要なことに、待つ必要さえありません。

第二に、あなたはlock1間違って使用しています。受信スレッドがイベントを設定し、すぐにクリアします。しかし、送信スレッドがまだ待機状態にあるという保証はありません。(前の問題との競合がこれをさらに問題にしていますが、それを修正しても問題は残ります。) マルチコア マシンでは、通常は時間内に到達しますが、「通常」はまったく問題にならないよりもさらに悪いです。スレッドプログラミングで。そのため、受信スレッドがすでにクリアを行った後、最終的に送信スレッドは待機状態になるため、永遠に待機します。一方、受信スレッドは、送信スレッドからの通知を待っていますが、これは決して起こりません。だからあなたは行き​​詰まっています。

今後の参考のためprintに、すべてのブロック操作、特に同期操作の前後にステートメントを追加すると、デバッグが大変になります。受信スレッドの最後のメッセージは「cv1 で待機中の受信」であり、送信スレッドの最後のメッセージは「send lock1 で待機中」と表示され、デッドロックがどこにあったかは明らかです。


とにかく、条件のない cv や、cv として使用しようとしているイベントを「修正」することが何を意味するのかさえわからないので、代わりに 2 つの cv を使用して適切なものを作成する方法を示します。 . この場合、両方の cv の条件として前後に反転するフラグを使用することもできます。

その間、あなたのコードをテストすることすらできない (たとえば、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 ベースでカウントされるため)。最後に。(もちろん、クライアントはreceiveAck終了する方法がないため永久にハングし、サーバーは無限ループとして記述したためです。)

于 2013-11-14T20:52:12.150 に答える