Как уменьшить использование памяти многопоточным кодом Python? - PullRequest
7 голосов
/ 10 января 2012

Я написал около 50 классов, которые я использую для подключения и работы с веб-сайтами с использованием механизации и потоков.Все они работают одновременно, но они не зависят друг от друга.Так что это означает 1 класс - 1 сайт - 1 поток.Это не очень элегантное решение, особенно для управления кодом, так как большая часть кода повторяется в каждом классе (но этого недостаточно, чтобы превратить его в один класс для передачи аргументов, поскольку некоторые сайты могут требовать дополнительной обработки извлеченных данных в середине методов- как «логин» - что другим может не понадобиться)Как я уже сказал, это не элегантно, но работает.Само собой разумеется, я приветствую все рекомендации, как написать это лучше, не используя 1 класс для каждого подхода веб-сайта.Добавление дополнительной функциональности или общего управления кодом каждого класса является непростой задачей.

Однако я обнаружил, что каждый поток занимает около 8 МБ памяти, поэтому при 50 работающих потоках мы рассматриваем использование около 400 МБ.Если бы он работал в моей системе, у меня не было бы проблем с этим, но, поскольку он работает на VPS только с 1 ГБ памяти, это становится проблемой.Можете ли вы сказать мне, как уменьшить использование памяти, или есть ли другой способ работать с несколькими сайтами одновременно?

Я использовал эту программу быстрого тестирования Python, чтобы проверить, хранятся ли данные в переменных моего приложениякоторый использует память или что-то еще.Как видно из следующего кода, он обрабатывает только функцию sleep (), но каждый поток использует 8 МБ памяти.

from thread import start_new_thread
from time import sleep

def sleeper():
    try:
        while 1:
            sleep(10000)
    except:
        if running: raise

def test():
    global running
    n = 0
    running = True
    try:
        while 1:
            start_new_thread(sleeper, ())
            n += 1
            if not (n % 50):
                print n
    except Exception, e:
        running = False
        print 'Exception raised:', e
    print 'Biggest number of threads:', n

if __name__ == '__main__':
    test()

Когда я запускаю это, вывод:

50
100
150
Exception raised: can't start new thread
Biggest number of threads: 188

И, удалив строку running = False, я могу затем измерить свободную память с помощью команды free -m в оболочке:

             total       used       free     shared    buffers     cached
Mem:          1536       1533          2          0          0          0
-/+ buffers/cache:       1533          2
Swap:            0          0          0

Фактический расчет, почему я знаю, что он занимает около 8 МБ на поток, тогда просто делитсяделение разницы памяти, использованной до и во время выполнения вышеуказанного тестового приложения, деленное на максимальное количество потоков, которое ему удалось запустить.

Вероятно, это только выделенная память, потому что, глядя на top, процесс python используеттолько около 0,6% памяти.

Ответы [ 4 ]

4 голосов
/ 10 января 2012
2 голосов
/ 10 января 2012

Использование «одного потока на запрос» нормально и легко для многих случаев использования. Однако для этого потребуется много ресурсов (как вы уже видели).

Лучше использовать асинхронный, но, к сожалению, он намного сложнее.

Некоторые подсказки в этом направлении:

1 голос
/ 10 января 2012

Решение состоит в том, чтобы заменить код следующим образом:

1) Сделай что-нибудь.
2) Подождите, пока что-то случится.
3) Сделай что-нибудь еще.

С таким кодом:

1) Сделай что-нибудь.
2) Сделайте так, чтобы когда что-то происходило, что-то еще делалось.
3) Готово.

Где-то еще у вас есть несколько потоков, которые делают это:

1) Подождите, пока что-нибудь случится.
2) Обращайся, что бы ни случилось.
3) Перейти к шагу 1.

В первом случае, если вы ожидаете, что произойдет 50 событий, у вас будет 50 потоков, ожидающих 50 событий. Во втором случае у вас есть один ожидающий поток, который будет делать то, что нужно для выполнения этих 50 вещей.

Так что, не используйте поток, чтобы ждать, пока что-то произойдет. Вместо этого, сделайте так, чтобы когда это происходило, какой-то другой поток делал все, что нужно, чтобы сделать следующее.

0 голосов
/ 10 января 2012

Я не эксперт по Python, но, возможно, у меня есть несколько пулов потоков, которые контролируют общее количество активных потоков и передают «запрос» потоку, как только это будет сделано с предыдущим потоком.Запрос не обязательно должен быть объектом с полным потоком, достаточно данных, чтобы выполнить любой запрос.

Вы также можете структурировать его так, чтобы у вас был пул потоков A с N потоками, проверяющими веб-сайт, как только данныеизвлекается, передает данные в пул потоков B, где Y-потоки пересекают данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...