python队列没有传递正确的数据

时间:2012-05-12 09:45:36

标签: python queue python-multithreading

刚刚找到了帮助我调整pyftpdlib模块的Queue模块。我正在运行一个非常严格的FTP服务器,我的目标是限制可上传的文件名。这是为了防止人们上传他们想要的东西(它实际上是上传客户端的后端,而不是完整的FTP服务器)。

我在ftpserver Authorizer中有这个:

def fetch_worlds(queue, username):
    while queue.empty():
        worlds = models.World.objects.filter(is_synced=True, user__username=username)
        print worlds

        queue.put(worlds, timeout=1)

class FTPAuthorizer(ftpserver.DummyAuthorizer):

    def __init__(self):
        self.q = Queue.Queue()
        self.t = None # Thread
        self.world_item = None

    def has_perm(self, username, perm, path=None):

        print "Checking permission\n"

        if perm not in ['r','w']:
            return False

        # Check world name        
        self.t = threading.Thread(target=fetch_worlds, args=(self.q, username))
        self.t.daemon = True
        self.t.start()

        self.world_item = self.q.get() 

        print "WORLDITEM: %s" % self.world_item

        if path is not None:
            path = os.path.basename(path)
            for world in self.world_item:
                test = "{0}_{1}.zip".format(username, world.name)
                if path == test:
                    print "Match on %s" % test
                    return True

        return False

我的问题是,在服务器启动后,我第一次存储文件时,它会进行初始数据库调用并正确地获取所有世界。但是当我然后添加另一个世界(例如,将is_synced=True设置为一个世界时,它仍会返回self.q.get()中的旧数据。每次上传文件时都会调用has_perm(),并且需要返回实时数据(检查是否允许文件)。

例如,全新的服务器:

  1. STOR file.zip,self.q.get()返回<World1, World2>
  2. 通过其他方法等更新数据库
  3. STOR file2.zip,在fetch_worlds内,print worlds返回<World1, World2, World3>self.q.get()返回<World1, World2>
  4. 刚刚找到了Queue模块,看起来它会有所帮助,但我无法正确实现。

    (也无法添加标记pyftpdlib

1 个答案:

答案 0 :(得分:0)

我认为这是可能发生的事情:

  1. 调用has_perm时,您创建一个Thread来查询数据库(?)以将元素添加到队列中
  2. 调用start后,对数据库的调用将花费一些时间
  3. 同时在你的主线程中输入了q.get,这将阻止。
  4. db调用结束,结果添加到队列
  5. 并立即通过阻止q.get
  6. 从队列中删除
  7. 队列现在为空,您的线程再次进入while循环并再次执行相同的查询并将结果放入队列。
  8. q.get的下一次调用将返回该内容而不是预期内容。
  9. 你知道,你可能在这里遇到一个竞争条件,因为你在循环中没有循环时向循环中添加一些东西已经很明显了。 你也假设你从队列中得到的元素是你之前放到它上面的结果。这不一定是真的。
    如果您拨打has_perm两次,这将导致两次调用fetch_worlds,其中一次呼叫的queue.empty()检查失败。所以只有一个结果会被放入队列中。现在你有两个线程等待q.get,但只有一个会得到一个结果,而另一个等待直到一个准备就绪......

    无论如何,

    has_perm看起来应该是阻止通话。