简体   繁体   English

多线程-在Python中使用条件和事件在两个线程之间交替

[英]Multithreading - Alternating between two threads using Conditions and Events in Python

I am trying to write a program using which I wish to alternate between two threads, thread1 and thread2. 我正在尝试编写一个程序,希望使用该程序在两个线程thread1和thread2之间交替。 The tricky part is that the thread should begin execution first must be thread1. 棘手的部分是,线程应该首先开始执行的必须是thread1。 This is the code I have so far: 这是我到目前为止的代码:

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

The problem I am currently facing is that initially the program does alternate between two threads (confirmed by the output on the console. But after an arbitrary number of iterations (usually between 20 and 80) the program just hangs and no further iterations are performed. 我当前面临的问题是,程序最初会在两个线程之间交替(由控制台上的输出确认),但是经过任意次迭代(通常在20到80之间)后,程序只是挂起,不再执行任何迭代。

There are at least two problems with your synchronization. 同步至少有两个问题。

First, you're using cv1 wrong. 首先,您使用的cv1错误。 Your receive thread has to loop around its cv, checking the condition and calling wait each time. 您的接收线程必须遍历其cv,检查条件并每次都调用wait Otherwise, you're just using a cv as a broken event + lock combination. 否则,您只是将简历用作损坏的事件+锁组合。 You don't have such a loop. 您没有这样的循环。 More importantly, you don't even have a condition to wait for. 更重要的是,您甚至没有条件等待。

Second, you're using lock1 wrong. 其次,您使用的lock1错误。 Your receive thread sets the event and then immediately clears it. 您的接收线程设置事件,然后立即将其清除。 But there's no guarantee that the send thread has gotten to the wait yet. 但是,不能保证发送线程已经等待。 (The race from the previous problem makes this more of a problem, but it's still a problem even if you fix that.) On a multi-core machine, it will usually get there in time, but "usually" is even worse than never in threaded programming. (与前一个问题的竞赛使这个问题成为更多问题,但是即使您将其解决也仍然是一个问题。)在多核计算机上,它通常会及时到达那里,但是“通常”甚至比从来没有更糟。在线程编程中。 So, eventually the send thread will get to the wait after the receive thread has already done the clear, and therefore it will wait forever. 因此,最终在接收线程已经完成清除之后,发送线程将进入等待状态,因此它将永远等待。 The receive thread, meanwhile, will be waiting to be notified by the send thread, which will never happen. 同时,接收线程将等待发送线程通知,这将永远不会发生。 So you're deadlocked. 所以你陷入僵局。

For future reference, adding print statements before and after every blocking operation, especially sync operations, would make this a lot to debug: you would see the receive thread's last message was "receive waiting on cv1", while the send thread's last message was "send waiting on lock1", and it would be obvious where the deadlock was. 供以后参考,在每个阻塞操作(尤其是同步操作)之前和之后添加print语句将使调试工作变得很多:您会看到接收线程的最后一条消息是“ receivewaiting on cv1”,而发送线程的最后一条消息是“发送在lock1上等待”,这显然是死锁所在的位置。


Anyway, I'm not sure what it would even mean to "fix" a cv with no condition, or an event that you're trying to use as a cv, so instead I'll show how to write something sensible with two cvs. 无论如何,我不确定“无条件”修复cv或您要用作cv的事件的含义,所以我将展示如何用两个cv编写有意义的内容。 。 In this case, we might as well just use a flag that we flip back and forth as the condition for both cvs. 在这种情况下,我们最好只使用来回翻转的标志作为两个cv的条件。

While I'm at it, I'll fix a couple other problems that made your code not even testable (eg, i is never initialized), and include the debugging information, and what I had to fill in to make this a complete example, but otherwise I'll try to leave your structure and irrelevant problems (like Client being an old-style class) intact. 在解决这个问题时,我将修复一些其他问题,这些问题使您的代码甚至无法测试(例如, 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()

And here's a test server for it: 这是一个测试服务器:

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)

Start the server, start the client, the server will count up to 672, the client will count up to 673 (since your code counts 1-based) with 673 balanced pairs of messages and a "File sent successfully !" 启动服务器,启动客户端,服务器将最多计数672,客户端将最多计数673(因为您的代码从1开始计数),其中包含673对平衡的消息对和“文件发送成功!”。 at the end. 在末尾。 (Of course the client will then hang forever because receiveAck has no way to finish, and the server because I wrote it as an infinite loop.) (当然,由于receiveAck无法完成,因此客户端将永远挂起,而由于我将其编写为无限循环,则服务器将永远挂起。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM