Python 集合正在被修改,然后似乎神奇地恢复

Python 集合正在被修改,然后似乎神奇地恢复,python,multithreading,set,sync,Python,Multithreading,Set,Sync,我正在尝试在不可靠通道的基础上构建一个可靠传输的通道(这是一个练习,不可靠通道会显式地丢弃一些数据包)。我有一个acks集合,其中包含(地址、序列号)对。当通过通道接收到ack时,它被添加到acks集合中,并通知一个条件变量: msg, addr = self.unreliable_channel.recv() if isinstance(msg, Ack): with self.acks_cond: self.acks.add((addr, msg.ack))

我正在尝试在不可靠通道的基础上构建一个可靠传输的通道(这是一个练习,不可靠通道会显式地丢弃一些数据包)。我有一个
acks
集合,其中包含
(地址、序列号)
对。当通过通道接收到ack时,它被添加到
acks
集合中,并通知一个条件变量:

msg, addr = self.unreliable_channel.recv()
if isinstance(msg, Ack):
    with self.acks_cond:
        self.acks.add((addr, msg.ack))
        print "{} got an ack: {} ({})".format(self.port, self.acks, hex(id(self.acks)))
        self.acks_cond.notify()
另一个线程正在侦听条件变量并检查线程中的ACK:

with self.acks_cond:
    max_wait = 2 * self.unreliable_channel.delay_avg
    start = time.time()
    while not ((addr, msg.seq) in self.acks) and (time.time() - start < max_wait):
        print "{}: self.acks is {} ({})".format(self.port, self.acks, hex(id(self.acks)))
        self.acks_cond.wait(0.1)
    print "{} waited for ack of {} from {}: {} ({})".format(self.port, msg.seq, addr, self.acks, hex(id(self.acks)))
    if (addr, msg.seq) in self.acks:
        print '!' * 10000
        # self.acks.remove((addr, msg.seq))
        return
'toplel'
是我在集合中加入的字符串,以确保它不是以某种方式被清空的)

有人知道什么会把事情搞砸吗

下面的代码转储:(尝试创建SSCCE,但似乎无法重现该行为——如果我得到一个,我将尝试更多)

导入线程
导入时间
从集合导入defaultdict,namedtuple
Message=namedtuple('Message',['seq','data']))
Ack=namedtuple('Ack',['Ack']))
类可靠通道:
''建立在不可靠频道之上,该频道支持
保证最终交付,每个目的地的FIFO交付
基础,无重复交付。”
def _初始(自身、不可靠信道):
self.unreliable_channel=不可靠_channel
#我们收到确认的一组(addr,seq)对
打印“初始化TOPLEL”
self.acks=set([“toplel”])
self.acks_cond=线程化.Condition()
self.seq=defaultdict(int)
self.port=self.unreliable\u channel.sock.getsockname()[1]
打印'self.port:{}'。格式(self.port)
#左撇子:我们启动的线程似乎无法修改self.acks以便单播可以看到它
self.listener=threading.Thread(target=self.listen)
self.listener.start()
def监听(self):
尽管如此:
msg,addr=self.unreliable_channel.recv()
如果存在(消息,确认):
使用self.acks\u cond:
self.acks.add((addr,msg.ack))
打印“{}得到一个确认:{}({})”.format(self.port,self.acks,hex(id(self.acks)))
self.acks_.cond.notify()
其他:
确认=确认(消息顺序)
自身不可靠信道单播(ack,addr)
打印{}得到消息{}并将{}发送回{}格式(self.port、msg、ack、addr)
def单播(self、msg、addr):
self.seq[addr]+=1#获取此消息的序列号
msg=消息(self.seq[addr],msg)
打印{}试图将消息{}发送到{}格式(self.port、msg、addr)
尽管如此:
#发送消息
self.unreliable_channel.unicast(消息,地址)
#等待带有超时的ack
使用self.acks\u cond:
最大等待时间=2*自身不可靠信道延迟平均值
开始=时间。时间()
而不是(self.acks中的(addr,msg.seq)和(time.time()-start

编辑:在做了更多的胡闹之后,两个线程中的集合似乎有时会在一段时间后“发散”。根据我在
listen
中的位置,对列表的修改有时会发生,但之后,似乎每个线程都在使用自己的集合副本(即,我尝试在两个线程中向集合添加内容)。

自我回答:知道这是相当愚蠢的事情。上面的
\uuuuu init\uuuu
方法启动了一个线程,并且它正在
多处理.Process
子类的
\uuuuuu init\uuuu
方法中运行。这意味着运行
listen
的线程是在一个进程中启动的,而运行
unicast
的线程是在一个单独的进程中运行的,我猜是一个重复的集合副本

我本应该早点意识到这一点,但是
id
s是相同的这一事实让我很反感——我想我认为一个分叉的进程会有自己的虚拟地址,所以它是相同地址的几率很低。没有想到它会重复使用相同的虚拟地址


tl;dr:
id(x)==id(y)
并不意味着如果你正在处理多个处理,那么
x
y
是同一个对象。

你是否尝试过检查和报告集合的id,以确保你是从同一个集合对象工作的?@AaronHall是的,上面的代码打印了它,它在我包含的两行输出中。也许我有可能弄乱了地址空间,得到了代表两个不同对象的相同虚拟地址,但我觉得这不太可能。我怀疑你的数据是否“发散”。但从我前面的问题可以看出,我现在有点TLDR。对不起!:)
10000 got an ack: set([(('192.168.1.7', 10001), 1), 'toplel']) (0x7f40a944ced0)
10000: self.acks is set(['toplel']) (0x7f40a944ced0)
import threading
import time
from collections import defaultdict, namedtuple

Message = namedtuple('Message', ['seq', 'data'])
Ack = namedtuple('Ack', ['ack'])

class ReliableChannel:
    '''Building on top of UnreliableChannel, this channel supports
    guarenteed eventual delivery, FIFO delivery on a per-destination
    basis, and no duplicated delivery.'''

    def __init__(self, unreliable_channel):
        self.unreliable_channel = unreliable_channel

        # a set of (addr, seq) pairs for which we've recieved acks
        print "INITIALIZING THE TOPLEL"
        self.acks = set(["toplel"])
        self.acks_cond = threading.Condition()

        self.seq = defaultdict(int)

        self.port = self.unreliable_channel.sock.getsockname()[1]
        print 'self.port: {}'.format(self.port)

        # leftoff: the thread we started can't seem to modify self.acks so that unicast can see it
        self.listener = threading.Thread(target=self.listen)
        self.listener.start()

    def listen(self):
        while True:
            msg, addr = self.unreliable_channel.recv()
            if isinstance(msg, Ack):
                with self.acks_cond:
                    self.acks.add((addr, msg.ack))
                    print "{} got an ack: {} ({})".format(self.port, self.acks, hex(id(self.acks)))
                    self.acks_cond.notify()
            else:
                ack = Ack(msg.seq)
                self.unreliable_channel.unicast(ack, addr)
                print '{} Got message {} and sent {} back to {}'.format(self.port, msg, ack, addr)

    def unicast(self, msg, addr):
        self.seq[addr] += 1  # get the sequence number for this message
        msg = Message(self.seq[addr], msg)
        print '{} Trying to send message {} to {}'.format(self.port, msg, addr)
        while True:
            # send a message
            self.unreliable_channel.unicast(msg, addr)

            # wait for an ack with a timeout
            with self.acks_cond:
                max_wait = 2 * self.unreliable_channel.delay_avg
                start = time.time()
                while not ((addr, msg.seq) in self.acks) and (time.time() - start < max_wait):
                    print "{}: self.acks is {} ({})".format(self.port, self.acks, hex(id(self.acks)))
                    self.acks_cond.wait(0.1)
                print "{} waited for ack of {} from {}: {} ({})".format(self.port, msg.seq, addr, self.acks, hex(id(self.acks)))
                if (addr, msg.seq) in self.acks:
                    print '!' * 10000
                    # self.acks.remove((addr, msg.seq))
                    return