Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用线程将数组分割成块,对每个块执行计算,并将返回的数组重新组装成一个数组_Python_Multithreading_Numpy - Fatal编程技术网

Python 使用线程将数组分割成块,对每个块执行计算,并将返回的数组重新组装成一个数组

Python 使用线程将数组分割成块,对每个块执行计算,并将返回的数组重新组装成一个数组,python,multithreading,numpy,Python,Multithreading,Numpy,我有一个很大的python数组,我想把它分解成块,然后对块执行计算,然后“重新组装”成一个数组。下面是我到目前为止所学的内容,我刚刚开始学习一般的线程和Python线程 def performCalc(binaryArray): # perform some operation rArray = blah * binaryArray return rArray def main(argv): numberOfThreads = 5 str(len(gr

我有一个很大的python数组,我想把它分解成块,然后对块执行计算,然后“重新组装”成一个数组。下面是我到目前为止所学的内容,我刚刚开始学习一般的线程和Python线程

def performCalc(binaryArray):

    # perform some operation
    rArray = blah * binaryArray
    return rArray

def main(argv):
    numberOfThreads = 5
    str(len(grey_arr) # 100,000 elements
    greyScaleChunks = np.array_split(grey_arr, numberOfThreads)
    for i in range(numberOfThreads):
        t = Thread(target=performCalc, args=(greyScaleChunks[i],))
        t.start()

    # take all the return values and later create one big array to be resized into matrix.

块的顺序很重要,我必须保持这一点。

如果你想用显式的
线程
对象来解决它,并且你想得到线程函数的结果,你需要抓住那些
线程
对象,这样你以后可以
连接它们并取出它们的结果。像这样:

ts = []
for i in range(numberOfThreads):
    t = Thread(target=performCalc, args=(greyScaleChunks[i],))
    ts.append(t)
    t.start()
for t in ts:
    t.join()
# When you get here, all threads have finished
另外,
Thread.run
的默认实现只调用您的
target
并丢弃结果。因此,您需要将返回值存储在主线程可以访问的位置。许多numpy程序通过向每个线程传递一个预先分配的数组来实现这一点,这样它们就可以填充它们,这对您的设计没有太大的改变,但这不是您的方向。当然,您可以传入任何其他可变对象以进行突变。或者设置一个全局变量,等等。但是你已经围绕着返回一个值来设计了它,这是一个很好的思考问题的方法,所以让我们坚持下去。最简单的方法是将
线程子类化:

class ReturningThread(threading.Thread):
    def run(self):
        try:
            if self._target:
                self._result = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs

    def join(self):
        super().join()
        return self._result
这是未经测试的代码,但应该可以工作。(我在实际代码中也做过类似的事情,但更复杂的是,允许
join
正确处理超时;在这里,我非常简单,只是在
run
方法中添加了
\u result=
,并在
join
中返回它)

因此:

现在您有了一个可以堆叠在一起的数组列表


然而,我在上面所做的基本上是把每一个线程变成一个半途而废的未来。从概念上讲,只使用实际的期货可能更简单。这意味着我们现在使用的是一个线程池,我们实际上不需要它,因为每个线程只有一个任务。性能成本可能可以忽略不计(您在实际工作上花费的时间比排队要多得多,或者您根本不想以这种方式执行任务),但是,更重要的是,我们增加了大量额外的复杂性(在经过良好测试的stdlib模块中),以降低代码的复杂性;这是否值得,取决于你。无论如何:

with concurrent.futures.ThreadPoolExecutor(max_workers=numberOfThreads) as x:
    results = x.map(performCalc, greyScaleChunks)
这将处理创建5个线程,为每个
performCalc(chunk)
创建一个作业,将5个作业划分为5个线程,连接线程,并按顺序收集5个作业的结果,因此您所要做的就是将结果堆叠起来


使用执行器的另一个优点是,如果由于GIL,您的代码没有从线程并行性中获益(在您的情况下,这不太可能是一个问题,您应该将大部分时间花在超过20000行的numpy操作上,该操作将在GIL发布后运行,但显然您必须进行测试以验证这一点是否正确),您可以非常轻松地切换到进程:只需将
ThreadPoolExecutor
更改为
ProcessPoolExecutor
,就完成了


您的参数和返回值可能无法以默认方式在进程之间复制或共享,或者这样做的成本太高,以至于它扼杀了并行性的所有好处,但您可以通过一个单词的更改来测试这一点,然后仅在出现问题时处理它,仍然是一场胜利。

您可以使用基本上未记录的
线程池(在中提到)及其
map\u async()

import numpy as np
from pprint import pprint
from multiprocessing.pool import ThreadPool
import threading

blah = 2

def performCalc(binaryArray):
    # perform some operation
    rArray = blah * binaryArray
    return rArray

def main(data_array):
    numberOfThreads = 5
    pool = ThreadPool(processes=numberOfThreads)

    greyScaleChunks = np.array_split(data_array, numberOfThreads)
    results = pool.map_async(performCalc, greyScaleChunks)
    pool.close()
    pool.join()  # Block until all threads exit.

    # Final results will be a list of arrays.
    pprint(results.get())

grey_arr = np.array(range(50))
main(grey_arr)
打印结果:

[数组([0,2,4,6,8,10,12,14,16,18]),
数组([20,22,24,26,28,30,32,34,36,38]),
数组([40,42,44,46,48,50,52,54,56,58]),
数组([60,62,64,66,68,70,72,74,76,78]),
数组([80,82,84,86,88,90,92,94,96,98])]

在这里,线程是错误的方法。你的程序花在上下文切换上的时间是它实际计算的时间;一次只能运行一个线程。您希望
多处理
模块将计算分散到多个进程,这些进程可以在多个处理器/内核上并行执行。@chepner这看起来像是在使用numpy数组。如果是这样的话,他的大部分CPU时间将花费在numpy中,GIL将被解锁,因此线程工作正常。使用
concurrent.futures中的
ThreadPoolExecutor
可能更简单。实际上,这里不需要线程池,因为每个任务有一个线程,但它仍然可以很容易地分配作业并按顺序收集结果,这正是您遇到问题的地方。@chepner另外,5个线程与5个进程的实际上下文切换是相同的。5个进程在4个核上的竞争将和5个线程一样糟糕,而使用8个核的5个进程将与5个线程一样理想地并行。@chepner是的,线程被限制为单个进程,但每个现代操作系统都在进程内处理多线程,因此线程不限于单个核。在CPython中,如果您在Python本身中执行重要的CPU工作,则在执行每个步骤时需要保持GIL(全局解释器锁),这意味着在任何时候,除了一个线程之外,所有线程都在等待获取该GIL,因此您几乎没有并行性(这些锁操作会增加成本)-但是,如果您在一个长时间释放GIL的C扩展中工作,这不是问题。如果线程池的费用(更概念化而非性能)不是问题,我认为
ThreadPoolExecutor
ThreadPool
(或
dummy.pool
)更清晰。不仅仅是因为它有文档记录,而其他的没有(甚至可能在未来的版本中消失)
import numpy as np
from pprint import pprint
from multiprocessing.pool import ThreadPool
import threading

blah = 2

def performCalc(binaryArray):
    # perform some operation
    rArray = blah * binaryArray
    return rArray

def main(data_array):
    numberOfThreads = 5
    pool = ThreadPool(processes=numberOfThreads)

    greyScaleChunks = np.array_split(data_array, numberOfThreads)
    results = pool.map_async(performCalc, greyScaleChunks)
    pool.close()
    pool.join()  # Block until all threads exit.

    # Final results will be a list of arrays.
    pprint(results.get())

grey_arr = np.array(range(50))
main(grey_arr)