锁定对资源的访问的哪种方式最适合/正确?

2024-10-02 18:17:13 发布

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

我正在构建一个多线程程序,因为它有大量的I/O。我想构建一个队列,该队列不断地填充来自数据库的部分信息,并在数据不足时进行补充。在循环结束时,所有线程都将检查队列是否需要补充。以下哪一项最准确(请注意,这是psedou代码,因为我只对更高级别的概念感兴趣)

另外,请原谅,正如您从代码中看到的,我在名称空间方面也有点困难。我已经做了大量的研究,大量的测试,我仍然在努力,所以任何帮助都是非常感谢的

# Option 1 - lock within the thread
class Thread():
    ...
    def run():
        # use an item off the queue
        with lock:
            replenish_queue()

lock = threading.Lock()
def replenish_queue():
    #check if queue needs replenishing

# ----------------------------------------

# Option 2 - lock within function
class Thread():
    ...
    def run():
        # use an item off the queue
        replenish_queue():

lock = threading.Lock()
def replenish_queue():
    with lock:
        #check if queue needs replenishing

Tags: therun代码anlock队列queueuse
1条回答
网友
1楼 · 发布于 2024-10-02 18:17:13

对于大量并发数据库写入,我认为主要的瓶颈之一实际上可能是sqlite本身。写锁发生在sqlite数据库级别(http://www.sqlite.org/faq.html

这与其他RDBMS不同,例如Postgres,后者提供表级的更新锁定,与MVCC结合使用,后者在更新方面意味着表的UPDATE将锁定它进行写入,不会阻止其他SELECT的运行

因此,无论您是通过线程还是多个进程来完成,尝试同时写入同一sqlite数据库本质上都是一个串行进程,因为sqlite需要在写入期间对整个数据库执行写锁

但是,您可以对sqlite数据库进行并发读取。因此,如果您的使用模式是这样的:您可以执行一系列读取、处理它们(从内部队列或类似队列),然后串行地执行写入(因为sqlite实际上在数据库上强制执行一个mutex来执行写入),那么这可能是最佳的

就队列本身而言,正如johnmee所提到的,生成器绝对是一种选择,而且可能是最好的,这取决于您确切的数据需求

然而,另一个值得考虑的是使用数据库本身作为队列,SQL表通常擅长于此;我一直在Postgres中这样做,我怀疑它们在sqlite中也同样有效。如果您的需求在一个表中,或者可以很容易地合并到一个队列中,那么这当然是最有效的。如果您的需求包括大量不容易JOIN编辑的不同表,或者来自不同数据库的表,那么在这两者之间需要有一个ETL层来将其转换为队列形式,这在当时可能是不值得的

如果使用DB作为队列,使用sqlite库(比如说,sqlite3),它确实非常适合,那么您可以声明您的cursor,并可以将它作为迭代器逐个遍历它,或者使用fetchmany和一个size参数,该参数相当于您要立即处理的批,比如说100

然后可以将其打包到另一个进程(即使用multiprocessing)来完成工作,同时主线程获取下一个块。不过,首先让一个线程处理每个数据块,看看这是否有帮助,因为通常情况下,是DB交互占用了实时时间,看看主线程是否可以在一个可接受的时间内自行处理这些数据块。如果没有,那么你可以把它分给一群工人,然后再把他们联系起来

当光标用完了要获取的块时,您就完成了主读取工作,而分叉进程(或者主进程,如果您要走这条路线)则一直在进行处理,一旦这些都完成了,您就可以在主线程中连续地更新它们

有时确实需要多个线程/进程,尤其是在执行大量I/O时,但情况并非总是这样,有时计划周密的/分块的数据库获取可以帮助解决很多问题,避免增加多线程/多处理的额外复杂性,因此我建议从这一点开始,只有在实际观察到的性能要求时才从这一点开始构建

编辑:

我从其中一条评论中看到,您已经在使用数据库排队了。如果你的意思是我上面所说的,那么我认为分批获取和修补以找到最佳大小应该有助于提高性能(减少到DB的往返次数=减少开销,即使使用本地DB),并且肯定会有助于降低内存消耗,因为,如果队列中有1000万个项目,一次只能获取size个项目

相关问题 更多 >