如何对Python锁进行排队?

2024-06-16 15:05:57 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有办法让python锁排队?到目前为止,我一直在我的代码中假设线程.锁定对队列进行操作。它看起来就像是把锁给了一个随机储物柜。这对我不好,因为我工作的程序(游戏)高度依赖于以正确的顺序获取消息。python中有排队锁吗?如果是这样,我会损失多少处理时间?在


Tags: 代码程序游戏消息高度队列顺序时间
3条回答

我偶然发现这篇文章是因为我有类似的要求。至少我是这么想的。在

我担心如果锁没有按FIFO的顺序释放,线程很可能会被饿死,这对我的软件来说是可怕的。在

读了一会儿之后,我消除了我的恐惧,意识到大家都在说:如果你想要这个,那你就错了。另外,我确信你可以依靠操作系统来完成它的工作,而不会让你的线程饿死。在

为了达到这一点,我做了一些挖掘工作,以便更好地理解锁在Linux下是如何工作的。我先看一下PultPosits(POSIX线程)的GLUBC源代码和规范,因为我在Linux上工作在C++上。我不知道Python是否在幕后使用pthreads,但我认为可能是这样。在

在周围的多个pthreads引用中,我没有找到任何关于解锁顺序的规范。在

我发现:Linux上pthreads中的锁是使用名为futex的内核特性实现的。在

http://man7.org/linux/man-pages/man2/futex.2.html

http://man7.org/linux/man-pages/man7/futex.7.html

其中第一页的参考链接可将您引向此PDF:

https://www.kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf

它解释了一些解锁策略,以及futexes如何在Linux内核中工作和实现,等等。在

在那里我找到了我想要的。它解释了futexes在内核中的实现方式,使得解锁大多以FIFO顺序进行(以增加公平性)。但是,这并不能保证,而且有可能一个线程可能会在一段时间内跳过一行。它们允许这样做不会使代码复杂化,并考虑到它们所获得的良好性能,而不会因为执行FIFO命令的极端措施而失去性能。在

所以基本上,你所拥有的是:

POSIX标准对互斥锁的锁定和解锁顺序没有任何要求。任何实现都可以随心所欲,因此如果您依赖于此顺序,您的代码将无法移植(即使在同一平台的不同版本之间也是如此)。

pthreads库的Linux实现依赖于一种称为futex的特性/技术来实现互斥锁,它主要尝试对互斥锁进行FIFO风格的解锁,但不能保证会按照这个顺序进行。

是的,您可以使用线程ID列表创建一个FIFO队列:

FIFO = [5,79,3,2,78,1,9...]

您可以尝试获取锁,如果不能,则将尝试线程的ID(FIFO.insert(0,threadID))推到队列的前面,每次释放锁时,确保如果一个线程想要获得锁,它必须在队列的末尾有线程ID(threadID == FIFO[-1])。如果线程在队列末尾有线程ID,那么让它获取锁,然后将其弹出(FIFO.pop())。必要时重复。在

我完全同意那些声称你可能在用一种没有结果的方式来思考这个问题的评论。锁提供了序列化,而且根本不打算提供排序。执行命令的bog标准、简单、可靠的方法是使用Queue.Queue

CPython让操作系统来决定获取锁的顺序。在大多数系统中,这似乎或多或少是“随机”的。这是无法改变的。在

也就是说,我将展示一种实现“FIFO锁”的方法。这既不难也不容易——介于两者之间——你不应该使用它;—)恐怕只有你能回答你的“我会损失多少处理时间?”问题-我们不知道您使用锁的程度,也不知道您的应用程序会引发多少锁争用。不过,通过研究这段代码,您可以得到一个粗略的感觉。在

import threading, collections

class QLock:
    def __init__(self):
        self.lock = threading.Lock()
        self.waiters = collections.deque()
        self.count = 0

    def acquire(self):
        self.lock.acquire()
        if self.count:
            new_lock = threading.Lock()
            new_lock.acquire()
            self.waiters.append(new_lock)
            self.lock.release()
            new_lock.acquire()
            self.lock.acquire()
        self.count += 1
        self.lock.release()

    def release(self):
        with self.lock:
            if not self.count:
                raise ValueError("lock not acquired")
            self.count -= 1
            if self.waiters:
                self.waiters.popleft().release()

    def locked(self):
        return self.count > 0

下面是一个小的测试驱动程序,可以用明显的方式更改它来使用QLockthreading.Lock

^{pr2}$

在我刚才的框中,使用threading.Lock运行了3次,产生了以下输出:

BACDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSUVWXYZT
ABCEDFGHIJKLMNOPQRSTUVWXYZ

所以这当然不是随机的,但也不是完全可以预测的。使用QLock运行它,输出应该始终是:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

相关问题 更多 >