为什么线程会增加处理时间?

时间:2015-03-17 17:22:36

标签: python multithreading python-2.7 python-multithreading

我正在进行多任务处理基本的2-D DLA模拟。扩散限制聚合(DLA)是指粒子在接触当前聚合时执行随机游走和聚合的时间。

在模拟中,每步都有10,000个粒子走向随机方向。我使用一个工作池和一个队列来喂它们。我用粒子列表给它们喂食,工人在每个粒子上执行方法.updatePositionAndggregate()

如果我有一个工人,我用一个10.000颗粒的清单喂它,如果我有两个工人,我给它们提供每个5000个颗粒的清单,如果我有3个工人,我给它们提供一个3.333的清单每个粒子等等。

我现在向你展示一些工作人员的代码

class Worker(Thread):
    """
    The worker class is here to process a list of particles and try to aggregate
    them.
    """

    def __init__(self, name, particles):
        """
        Initialize the worker and its events.
        """
        Thread.__init__(self, name = name)
        self.daemon = True
        self.particles = particles
        self.start()

    def run(self):
        """
        The worker is started just after its creation and wait to be feed with a
        list of particles in order to process them.
        """

        while True:

            particles = self.particles.get()
            # print self.name + ': wake up with ' + str(len(self.particles)) + ' particles' + '\n'

            # Processing the particles that has been feed.
            for particle in particles:
                particle.updatePositionAndAggregate()

            self.particles.task_done()
            # print self.name + ': is done' + '\n'

在主线程中:

# Create the workers.
workerQueue = Queue(num_threads)
for i in range(0, num_threads):
    Worker("worker_" + str(i), workerQueue)

# We run the simulation until all the particle has been created
while some_condition():

    # Feed all the workers.
    startWorker = datetime.datetime.now()
    for i in range(0, num_threads):
        j = i * len(particles) / num_threads
        k = (i + 1) * len(particles) / num_threads

        # Feeding the worker thread.
        # print "main: feeding " + worker.name + ' ' + str(len(worker.particles)) + ' particles\n'
        workerQueue.put(particles[j:k])


    # Wait for all the workers
    workerQueue.join()

    workerDurations.append((datetime.datetime.now() - startWorker).total_seconds())
    print sum(workerDurations) / len(workerDurations)

所以,我打印等待工人终止任务的平均时间。我做了一些不同线程编号的实验。

| num threads | average workers duration (s.) |
|-------------|-------------------------------|
| 1           | 0.147835636364                |
| 2           | 0.228585818182                |
| 3           | 0.258296454545                |
| 10          | 0.294294636364                |

我真的很想知道为什么添加工人会增加处理时间,我认为至少有2名工人可以减少处理时间,但是从.14s开始大幅增加。到0.23s。你能解释一下为什么吗?

编辑: 那么,解释是Python线程实现,有没有办法让我可以拥有真正的多任务处理?

4 个答案:

答案 0 :(得分:4)

这种情况正在发生,因为线程不能同时执行,因为GIL(全局解释器锁定)一次只能执行一个线程。

当你产生一个新线程时,除了这个线程之外,所有东西都会冻结。当它停止时,另一个被执行。产卵线程需要很多时间。

友好的说法,代码并不重要,因为任何使用100个线程的代码都是SLOWER而不是使用Python中的10个线程的代码(如果更多的线程意味着更高的效率和更快的速度,并非总是如此。)

以下是Python docs的确切引用:

  

CPython实施细节

     

在CPython中,由于Global Interpreter Lock,只有一个线程可以同时执行Python代码(即使某些面向性能的库可能会克服此限制)。如果您希望应用程序更好地利用多核计算机的计算资源,建议您使用multiprocessingconcurrent.futures.ProcessPoolExecutor。但是,如果要同时运行多个I / O绑定任务,则线程仍然是一个合适的模型。

Wikipedia about GIL

StackOverflow about GIL

答案 1 :(得分:2)

python中的线程(至少在2.7中)不会因GIL而同时执行:https://wiki.python.org/moin/GlobalInterpreterLock - 它们在单个进程中运行并共享CPU,因此您无法使用线程来加速计算。

如果要使用并行计算来加速计算(至少在python2.7中),请使用进程 - 包multiprocessing

答案 2 :(得分:2)

这是由于Python的全局解释器锁定。不幸的是,Python中的GIL线程会阻塞I / O,因此永远不会超过1个CPU内核的使用率。看看这里让您开始了解GIL:https://wiki.python.org/moin/GlobalInterpreterLock

检查正在运行的进程(例如,Windows中的任务管理器),并注意到Python应用程序只使用了一个核心。

我建议在Python中查看多处理,这不受GIL的阻碍:https://docs.python.org/2/library/multiprocessing.html

答案 3 :(得分:-1)

实际创建另一个线程并开始处理它需要时间。由于我们无法控制调度程序,因此我愿意打赌这两个线程都安排在同一个核心上(因为工作量太小),因此您需要添加创建所需的时间线程并没有完成并行处理