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