如何控制多线程中的内存使用?

tom*_*wel 3 python multithreading memory-management image-processing out-of-memory

我正在使用多线程来处理图像。

它在我有足够内存的计算机上运行良好(处理大量图像时增加2~3 GB),但我的服务器只有1GB内存,代码无法正常运行。

有时以 结尾Segmentation fault,有时:

Exception in thread Thread-13:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "passportRecognizeNew.py", line 267, in doSomething
  ...
Run Code Online (Sandbox Code Playgroud)

代码:

import threading

def doSomething(image):
    # picture processing code
    print("processing over")

threads = []

for i in range(20):
    thread = threading.Thread(target=doSomething, args=("image",))
    threads.append(thread)

for t in threads:
    t.setDaemon(True)
    t.start()

t.join()

print("All over")
Run Code Online (Sandbox Code Playgroud)

如何解决这个问题或有什么方法可以控制内存使用?

Gho*_*ica 5

我认为你从错误的角度看待这个问题。你的代码启动了n 个线程。然后,这些线程执行您为它们定义的工作。

如果这项工作需要他们分配大量内存 - 该上下文“外部”的任何内容应该对此做什么?应该发生什么?是否应该杀死一些线程?是否应该在 C 代码深处的某处malloc……不发生……然后呢?

我想说的是:你的问题很可能是你只是启动了太多的线程。

因此,答案是:不要在破坏它们之后尝试修复它们- 最好确保您根本没有破坏它们:

  • 仔细分析,以了解您的应用程序;这样您就可以评估单个线程需要多少内存来完成其“工作”
  • 然后更改您的“主”程序以查询其运行的硬件(例如:检查可用内存和可用物理 CPU 的数量)
  • 并根据该评估,根据上述硬件详细信息启动应该工作的线程数

除此之外:这是非常常见的模式。开发人员正在开发一台“强大”的机器;他隐含地假设运行他的产品的任何目标系统都将具有相同或更好的特性。但这根本不是真的。

换句话说:当您不知道代码运行时的硬件是什么样子时,那么只有一件合理的事情要做:首先获取这些知识。然后根据真实数据做不同的事情。


tom*_*wel 5

GhostCat 的帮助下,我使用以下代码来解决内存使用问题。

import Queue
import threading
import multiprocessing
import time
import psutil


class ThreadSomething(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            # check available memory
            virtualMemoryInfo = psutil.virtual_memory()
            availableMemory = virtualMemoryInfo.available

            print(str(availableMemory/1025/1024)+"M")

            if availableMemory > MEMORY_WARNING:
                # image from queue
                image = self.queue.get()

                # do something
                doSomething(image)

                # signals to queue job is done
                self.queue.task_done()
            else:
                print("memory warning!")

def doSomething(image):
    # picture processing code, cost time and memory
    print("processing over")

# After testing, there seems no use to create threads more than CPU_COUNT, 
# execution time is not reduce.
CPU_COUNT = multiprocessing.cpu_count()
MEMORY_WARNING = 200*1024*1024  # 200M

images = ["1.png", "2.png", "3.png", "4.png", "5.png"]
queue = Queue.Queue()

def main():
    # spawn a pool of threads, and pass them queue instance
    for i in range(CPU_COUNT):
        t = ThreadSomething(queue)
        t.setDaemon(True)
        t.start()

    # populate queue with data
        for image in images:
            queue.put(image)

    # wait on the queue until everything has been processed
    queue.join()

start = time.time()
main()
print 'All over. Elapsed Time: %s' % (time.time() - start)
Run Code Online (Sandbox Code Playgroud)

我使用psutil模块来获取可用内存。

参考代码:yosemitebandit/ibm_queue.py

我的问题中的代码存在创建线程超过CPU_COUNT.