LINUX.ORG.RU

Скрипт отработал на 100%, но не выгрузился. Multithreading причина? Как лечить?

 


0

2

Есть скрипт на Python 2.7. Он запускает 70 тредов, они скачивают веб-страницы, грузят в базу. Все делается корректно, отрабатывает до последней строчки кода , там для контроля В ПОСЛЕДНЕЙ СТРОКЕ вывод на экран “Всё сделано!” - это выводится на экран,ОК .

Но скрипт зависает после отработки своей последней строки!!

Другие подобные скрипты работают, компьютер без глюков, Debian 8.

Даже идей нет почему в самом конце скрипт не выгружается из памяти... Треды заканчиваются вроде корректно:

while not workQueue.empty():
    pass
exitFlag = 1
for t in threads:
    t.join(timeout_for_job_of_one_Thread)

С чем это может быть связано? Поскольку скрипт запускается кроном- как его прихлопнуть корректно , не по таймауту? Условно, скрипт доходит до места “Всё сделано!” и далее запускает дочерний скрипт который убивает родительский по PID?



Последнее исправление: beastie (всего исправлений: 1)

чудеса

поставил в конец скрипта sys.exit('The end!')

скрипт выводит 'The end!' и все равно подвисает!

kubikk
() автор топика
Ответ на: чудеса от kubikk

еще чуднее

добавил в самый конец

os._exit()

видно что скрипт доходит до этого места...и всё. подвисает

kubikk
() автор топика
Ответ на: еще чуднее от kubikk

А что в run потоков? Скорее всего они подвисают, в цикле, например. Проверка какого-нибудь флага/той же очереди есть?

igor87
()
Ответ на: комментарий от igor87

я не очень знаю мультитрединг. делал все по схемам из учебников, вроде все соответсвует.

«А что в run потоков? » не совсем вас понял. Вот есть потоки. Они выполняют некую функцию, и «вычерпывают» очередь с данными , которые должны отработаться.

сдается мне у меня все корректно- очередь ВРОДЕ пуста в конце, потоки ВРОДЕ БЫ закончили работу.

меня смущает еще то что пока я явно не добавлял время жизни потока вроде все работало t.join()

потом стал делать вот так t.join(timeout_for_job_of_one_Thread)

но может случайное совпадение?

А как можно посмотреть вконце ситуацию- нет ли потока «застрявшего» где то, то есть «живого»? после всех этих:

exitFlag = 1

for t in threads:

t.join(timeout)

???

kubikk
() автор топика

можешь еще с strace-ом поупражняться

ananas ★★★★★
()
Ответ на: комментарий от Deleted

кода много.

упростим задачу - как товарищ выше предлагает: =А что в run потоков? Скорее всего они подвисают, в цикле, например=

вот отработали мои потоки ВРОДЕ, и ВРОДЕ очередь пуста.Как можно посмотреть есть ли еще живой поток? и может ли вообще такое быть после отработки фрагмента кода

exitFlag = 1
for t in threads:
t.join(timeout_for_job_of_one_Thread)

??

kubikk
() автор топика

os.kill(os.getpid(), 9) ?

скрипт какой то неубивающийся. попробую прихлопнуть как

os.kill(os.getpid(), 9)

но странно же.

kubikk
() автор топика
Ответ на: комментарий от kubikk

«А что в run потоков? » не совсем вас понял. Вот есть потоки. Они выполняют некую функцию, и «вычерпывают» очередь с данными , которые должны отработаться.

сдается мне у меня все корректно- очередь ВРОДЕ пуста в конце, потоки ВРОДЕ БЫ закончили работу.

Я про то, что в потоках не завершился run. Например, из-за бесконечного цикла. Условия завершения либо нет, либо оно не выполнилось.

меня смущает еще то что пока я явно не добавлял время жизни потока вроде все работало t.join()

потом стал делать вот так t.join(timeout_for_job_of_one_Thread)

но может случайное совпадение?

А вот тут уже я не понял. Что имеется ввиду под «я явно не добавлял время жизни потока»? join с указанием времени? Тогда не факт, что потоки завершались - join выжидал указанное время и возвращался в основной процесс. Без указания времени join ждёт завершения потока, с указанием - либо завершение, либо указанное время - что раньше наступит.

А как можно посмотреть вконце ситуацию- нет ли потока «застрявшего» где то, то есть «живого»?

htop, F5(Tree). Покажет список всех запущенных потоков процесса. Если поток завершился, то, по идее, его в том списке быть не должно.

t.isAlive() вернёт статус потока(boolean).

А вообще, присоединяюсь к просьбам полного листинга.

igor87
()

даже не зная питона, очевидно что ошибка в «for t in threads: t.join(timeout_for_job_of_one_Thread)» - join за timeout может и не получится. И по завершению цикла, очевидно что остались N живых потомков. А заняты они возможно общением с базой или чтением особо-интересных веб страниц и на поднятый флаг им вдоль.

MKuznetsov ★★★★★
()
Последнее исправление: MKuznetsov (всего исправлений: 1)

1) Божеее, кем надо быть, чтобы насоздавать 70 тредов в ПИТОНЕ, если они всё равно выполняются по очереди (ТС, набери в гугле GIL python)?!

2) вангую, что у ТС в тредах проверяется в цикле exitFlag. Но этот самый exitFlag он выставляет в главном треде. Если бы это была джава, то, согласно её модели памяти, ни один тред не увидит новых данных в exitFlag, если он не объявлен с volatile и если не было синхронизации.

Короче, для оптимизации каждый тред копирует к себе все не-volatile локальные переменные и не знает, что другие треды что-то в них изменяют, пока его кто-нибудь не дёрнет и не заставит синхронизироваться. Но это всё в джава, а как это работает в питоне и как это лечить, лень посмотреть, хотя дел, скорее всего, на 15 минут.

Hrenomoto
()
Ответ на: комментарий от MKuznetsov

спасибо вам. похоже вы правы «join за timeout может и не получится.» Но мне тогда нужен аналог такого джойна( :

А) поток у меня скачивает вебстраницу, да еще и через прокси. библиотека request.

экспериментально было обнаружено, что может случиться ситуация которая не отлавливается НИ через код ответа сервера (200, 500 и пр), НИ через таймаут!!! Я не знаю с чем такое связано. Может с какой то сверхкорявостью прокси, с большим числом редиректов на нем и пр

Просто из 1000 прокси найдется один который зависает так что на его зависании таймаут в Request не работает(

Б) я поэтому и запускал t.join(время жизни) как бы это обойти? запускать тред с каким то очень жестким временем таймаута, не привязанным к таймауту скачивания вебстраницы? но как. я где то видел вопрос типа «как запустить функцию навязав максимальное время ее работы» но как то было громоздко, вроде штатной возможности вызывать функции с таймаутом в питоне нет

kubikk
() автор топика
Ответ на: комментарий от Hrenomoto

=) Божеее, кем надо быть, чтобы насоздавать 70 тредов в ПИТОНЕ, если они всё равно выполняются по очереди (ТС, набери в гугле GIL python)?!=

ок. вот два варианта скачать тысячу страниц: А) делать все в лоб, одним потоком. у меня там прокся плюс урл. ждать скачки страницы можно до 5 секунд. Б) запустить 70 thread-ов. экспериментально я вижу что скорость скачки увеличивается на порядки.

что тут не так

kubikk
() автор топика
Ответ на: комментарий от Hrenomoto

«Короче, для оптимизации каждый тред копирует к себе все не-volatile локальные переменные и не знает, что другие треды что-то в них изменяют, пока его кто-нибудь не дёрнет и не заставит синхронизироваться.»

мой тред берет урл из очереди, скачивает его, кладет результат в global (!) список. потом когда ВСЕ треды ВСЁ отработают, глобальный список скармливается в sqlite (так громоздко ибо в тредах sqlite не умеет работать)

у меня нигде треды не меняют локальных переменных. о чем вы вообще?

kubikk
() автор топика
Ответ на: комментарий от igor87

интересно. спасибо. версия питона значения не имеет а вы его пробовали? стабильный?

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

kubikk
() автор топика
Ответ на: комментарий от Hrenomoto

вы какой то агрессивный. ваши ответы не несут добавленной информации, или несут ее как то криво.

жаль на этом форуме вас нельзя забанить, потому. флудер просто идите вон.

kubikk
() автор топика
Ответ на: комментарий от kubikk

если ты такой ранимый, можешь перейти в мой профиль и нажать «Игнорировать».

Hrenomoto
()
Ответ на: комментарий от kubikk

Версия Python >= 3.4, как написал выше. Есть внутренний проект, там как раз использую asyncio и aiohttp, но серверную часть, с клиентской не игрался. Пока в процессе разработки.

asyncio стабильный. Гвидо начал его делать(первоначальное название tulip) после релиза python 3.1, если не путаю.

aiohttp тоже нормально работает. Автор http://asvetlov.blogspot.ru/ . Он же делает еще несколько библиотек на основе asyncio. В частности, aiopg - коннект к PostgreSQL. Вот тут есть описание работы SQLAlchemy и aiopg вместе.

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

Не срабатывает

requests.get('https://github.com', timeout=5)
?

А если отдельно задать для коннекта и чтения

requests.get('https://github.com', timeout=(3.05, 27))
http://docs.python-requests.org/en/latest/user/advanced/#timeouts

igor87
()
Ответ на: комментарий от kubikk

А) поток у меня скачивает вебстраницу, да еще и через прокси. библиотека request.

возьмите libcurl, думаю биндинги к питону найдутся..она сама умеет качать в несколько потоков

Б) я поэтому и запускал t.join(время жизни) как бы это обойти?

а зачем вам результат завершения потока ? (ога - join для этого нужен). можно сразу cancel :-)

MKuznetsov ★★★★★
()
Ответ на: комментарий от Hrenomoto

иногда специалистам по джаве лучше жевать. у него io-bound задача, ему от GIL ни холодно ни жарко, но специалистам по джаве не понять, они ничего кроме «джава за 15 минут» не читали.

ТС: используй краулер уже и не морочь себе голову, вон scrapy отличный. если хочешь играться со своей поделкой: aio тебе не нужно, если ты с тредами заблудился. покажи уже наконец полный код, поможем тебе найти почему таймаут не срабатывает. скорее всего, ты действительно просто неправильно используешь requests.

val-amart ★★★★★
()
Ответ на: комментарий от val-amart

А) я о краулерах не просил. не можетет нести ничего кроме бреда- лучше смолчать, не так ли.

Б) на «ты» к себе подобных обращайтесь, ко мне на «вы», поняло?

kubikk
() автор топика
Ответ на: комментарий от kubikk

Б) на «ты» к себе подобных обращайтесь, ко мне на «вы», поняло?

Я не он, но пошёл ты в жопу.

proud_anon ★★★★★
()

GUI используется какое-нибудь? Tkinter конфликтует с потоками. Кроме того, он у меня даже sys.exit() иногда отлавливает и не дает выйти.

Поскольку скрипт запускается кроном- как его прихлопнуть корректно , не по таймауту? Условно, скрипт доходит до места “Всё сделано!” и далее запускает дочерний скрипт который убивает родительский по PID?

Надо просто убедиться, что все потоки завершаются успешно и главный цикл продолжает выполняться. Тогда программу можно спокойно завершить. А убивать что бы то ни было - это кривой вариант. Поставьте везде логгинг и посмотрите, где происходит зависание. Скорее всего, поток ждет до бесконечности, а главный цикл ждет завершения потока.

Deleted
()
Ответ на: комментарий от kubikk

o_0 первый день в интернете? если тебе помощь не нужна, нахуй с лора и проблема решена.

val-amart ★★★★★
()
Ответ на: комментарий от kubikk

нести ничего кроме бреда- лучше смолчать

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

false ★★★★★
()

1 убери таймаут из join 2 добавь таймауты в те места где выполняются запросы 3 профит

redixin ★★★★
()

Есть скрипт на Python 2.7. Он запускает 70 тредов, они скачивают веб-страницы, грузят в базу

Изврат какой-то. Используй scrapy - это асинхронный фреймворк на базе Twisted, специально заточенный для подобных задач.

Ну и если уж делать по кривому, на потоках, лучше использовать multiprocessing.pool.ThreadPool

anonymous
()
Ответ на: комментарий от kubikk

Божеее, кем надо быть, чтобы насоздавать 70 тредов в ПИТОНЕ, если они всё равно выполняются по очереди (ТС, набери в гугле GIL python)?!=

GIL никак не блокирует потоки, когда те уходят в io wait, так что мимо. А плохо это увеличенным расходом памяти и накладными расходами на бесполезные переключения контекста.

anonymous
()

Я бы рекомендовал использовать третий питон и констркцию:

from concurrent.futures import ThreadPoolExecutor, as_completed

def f(a, b):
    return a * b
    
with ThreadPoolExecutor(max_workers=100500) as pool:
    results = [pool.submit(f, a, b) for a in range(1, 11) for b in range(1, 11)]

    for future in as_completed(results):
        print(future.result())

Работает и завершается.

GoNaX ★★★
()
Ответ на: комментарий от Hrenomoto

1) Божеее, кем надо быть, чтобы насоздавать 70 тредов в ПИТОНЕ, если они всё равно выполняются по очереди (ТС, набери в гугле GIL python)?!

Для IO нормально, вобщет.

loz ★★★★★
()
Ответ на: комментарий от kubikk

Б) на «ты» к себе подобных обращайтесь, ко мне на «вы», поняло?

язабан

loz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.