Я использую Celery для постановки в очередь заданий из CGI-приложения, которое я создал. Как я это настроил, Celery заставляет каждое задание выполняться по одному или по два за раз, устанавливая CELERYD_CONCURRENCY = 1 или = 2 (чтобы они не перегружали процессор и не тормозили из-за потребления памяти). Очередь отлично работает благодаря совету, который я получил на StackOverflow.
Каждое из этих заданий занимает довольно много времени (примерно 30 минут серийно), но имеет неприятную возможность распараллеливания. По этой причине я использовал Pool.map, чтобы разделить его и выполнять работу параллельно. Он отлично работал из командной строки, и я получил время работы около 5 минут, используя новый многоядерный чип.
К сожалению, есть некоторое ограничение, которое не позволяет демоническому процессу иметь подпроцессы, и когда я запускаю причудливый параллельный код в очереди CGI, я получаю эту ошибку:
AssertionError: демонические процессы не могут иметь потомков
Я заметил, что у других людей есть были похожие вопросы, но я не могу найти ответ, который не потребовал бы полного отказа от Pool.map и усложнения код нити.
Каков правильный выбор дизайна здесь? Я могу легко запускать свои последовательные задания, используя очередь Celery. Я также могу выполнять гораздо более быстрые параллельные задания без очереди. Как мне подойти к этому и возможно ли получить то, что я хочу (как очередь, так и распараллеливание для каждого задания)?
Пара идей, которые у меня были (некоторые довольно хакерские):
- The job sent to the Celery queue simply calls the command line program. That program can use Pool as it pleases, and then saves the result figures & data to a file (just as it does now).
Downside: I won't be able to check on the status of the job or see if it terminated successfully. Also, system calls from CGI may cause security issues. - Obviously, if the queue is very full of jobs, I can make use of the CPU resources (by setting CELERYD_CONCURRENCY = 6 or so); this will allow many people to be "at the front of the queue" at once.
Downside: Each job will spend a lot of time at the front of the queue; if the queue isn't full, there will be no speedup. Also, many partially finished jobs will be stored in memory at the same time, using much more RAM. - Use Celery's @task to parallelize within sub-jobs. Then, instead of setting CELERYD_CONCURRENCY = 1, I would set it to 6 (or however many sub jobs I'd like to allow in memory at a time).
Downside: First of all, I'm not sure whether this will successfully avoid the "task-within-task" problem. But also, the notion of queue position may be lost, and many partially finished jobs may end up in memory at once. - Perhaps there is a way to call Pool.map and specify that the threads are non-daemonic? Or perhaps there is something more lightweight I can use instead of Pool.map? This is similar to an approach taken on another open StackOverflow question. Also, I should note that the parallelization I exploit via Pool.map is similar to linear algebra, and there is no inter-process communication (each just runs independently and returns its result without talking to the others).
- Throw away Celery and use multiprocessing.Queue. Then maybe there'd be some way to use the same "thread depth" for every thread I use (i.e. maybe all of the threads could use the same Pool, avoiding nesting)?
Заранее большое спасибо.