threading.Lock()性能问题

时间:2016-09-17 23:25:40

标签: python multithreading

我有多个帖子:

dispQ = Queue.Queue()
stop_thr_event = threading.Event()

def worker (stop_event):
    while not stop_event.wait(0):
        try:
            job = dispQ.get(timeout=1)
            job.waitcount -= 1
            dispQ.task_done()
        except Queue.Empty, msg:
            continue

# create job objects and put into dispQ here
for j in range(NUM_OF_JOBS):
    j = Job()
    dispQ.put(j)

# NUM_OF_THREADS could be 10-20 ish
running_threads = []
for t in range(NUM_OF_THREADS):
    t1 = threading.Thread( target=worker, args=(stop_thr_event,) )
    t1.daemon = True
    t1.start()
    running_threads.append(t1)


stop_thr_event.set()
for t in running_threads:
    t.join()

上面的代码给了我一些非常奇怪的行为。 我最终发现这是因为在没有锁定的情况下减少waitcount

我已在Job类self.thr_lock = threading.Lock()中添加了一个属性 然后我将其改为

with job.thr_lock:
    job.waitcount -= 1

这似乎解决了这种奇怪的行为,但看起来它的性能已经下降。

这是预期的吗?有没有办法优化锁定?
每个作业对象有一个全局锁而不是一个锁会更好吗?

2 个答案:

答案 0 :(得分:1)

关于"优化"的唯一方法线程化将打破可以同时执行的块或块工作中的处理。这主要意味着进行输入或输出(I / O),因为这是解释器释放Global Interpreter Lock(即GIL)的唯一时间。

实际上,除非符合上述条件,否则在添加线程时通常没有增益甚至净减速,除非满足上述条件。

如果您对所有共享资源使用单个全局锁定可能会更糟糕,因为它会使程序的某些部分在他们真的不需要这样做时等待,因为它不会区分什么需要资源,因此不必要的等待。

你可能会发现PyCon 2015的谈话David Beasley给出了感兴趣的Python Concurrency From the Ground Up标题。它包括线程,事件循环和协同程序。

答案 1 :(得分:1)

根据您的代码很难回答您的问题。锁确实有一些固有的成本,没有什么是免费的,但通常它很小。如果你的工作很小,你可能会考虑" chunking"就这些而言,相对于每个线程完成的工作量,你获得/释放调用的次数要少得多。

相关但独立的问题是相互阻塞的线程之一。如果许多线程正在等待相同的锁,您可能会注意到大的性能问题。在这里你的线程闲置着等待彼此。在某些情况下,这是无法避免的,因为共享资源是性能瓶颈。在其他情况下,您可以重新组织代码以避免此性能损失。

您的示例代码中有一些东西让我觉得它可能与实际应用程序有很大不同。首先,您的示例代码不会在线程之间共享作业对象。如果您不共享工作对象,则不应该对它们进行锁定。其次,编写完成后,您的示例代码可能不会清空队列。当你点击stop_thr_event.set()并将所有剩余的工作留在队列中时,它会立即退出,这是设计吗?