Уведомления

Группа в Telegram: @pythonsu

#1 Март 15, 2014 15:07:36

Galvanize
От:
Зарегистрирован: 2011-01-14
Сообщения: 67
Репутация: +  0  -
Профиль   Отправить e-mail  

проблема threading

Всем привет.

У меня возник насущный вопрос о кол-во одновременно работающих потоков. Я понимаю (может и не полностью) что такое GIL, и то, что 100, 200, 1000 потоков в питоне не будут работать нормально и просто зависнут.
Ко мне часто обращаются люди с просьбой написать всякие сканеры портов, которые смогут работать “ОЧЕНЬ БЫСТРО”, и на мой вопрос “насколько быстро” отвечают “ну, потоков в 500-1000”. Я говорю, что это невозможно, а они приводят мне какие-то доводы о том, что какой-то другой автор смог такое сделать. Я конечно же, сразу думаю, что человека обманули, т.к. скорее всего там нет параллельной работы сразу всех потоков, а стоит некий пул, из которого эти потоки по очереди запускаются.
Так вот, я хотел бы уточнить как оно на самом деле бывает, и если все-таки там пул, то где можно посмотреть вразумительный пример на эту тему.

И ещё такой вопрос:
будет ли лучше сделать асинхронный однопоточный сканер, вместо многопоточного?

Всем плюсую, даже за ответы вида “ты что, дурак?” =)



Отредактировано Galvanize (Март 15, 2014 15:09:34)

Офлайн

#2 Март 15, 2014 16:14:22

Singularity
Зарегистрирован: 2011-07-28
Сообщения: 1387
Репутация: +  75  -
Профиль   Отправить e-mail  

проблема threading

ты что, дурак?

Офлайн

#3 Март 15, 2014 17:06:54

Galvanize
От:
Зарегистрирован: 2011-01-14
Сообщения: 67
Репутация: +  0  -
Профиль   Отправить e-mail  

проблема threading

Нужен просто оптимальный способ запускать 500 потоков, даже если они не будут работать параллельно и все вместе. Простой пример пула, если возможно, приведите.



Офлайн

#4 Март 15, 2014 19:17:40

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10016
Репутация: +  857  -
Профиль   Отправить e-mail  

проблема threading

threading не даёт скорости, просто изображает параллельную работу
для скорости используют multiprocessing

python.org. multiprocessing
python.org. примеры

Galvanize
и если все-таки там пул, то где можно посмотреть вразумительный пример на эту тему
всё можно посмотреть в исходниках



Отредактировано py.user.next (Март 15, 2014 19:19:17)

Офлайн

#5 Март 16, 2014 06:17:45

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

проблема threading

py.user.next
threading не даёт скорости, просто изображает параллельную работу
Это настоящие потоки. Просто питоновский код слишком часто синхронизируется. Если вы будете запускать из питона долго работающие скомпилированные модули то получите значительное ускорение от применения threading. multiprocessing от threading отличается организацией доступа к общим данным. Другое дело что 1000 потоков не имеют смысла. У вас на машине сколько ядер? ну 8 может 16. Грубо говоря надо 8 потоков или процессов, а остальное все за счет asincio http://docs.python.org/3.4/library/asyncio.html.

Вы сами легко сами можете заметить потребность в multiprocessing или threading. Посмотрите загрузку процессора. Как полностью загрузите одно ядро, отстреливайте второй процесс.



Отредактировано doza_and (Март 16, 2014 06:18:20)

Офлайн

#6 Март 16, 2014 06:50:49

JOHN_16
От: Россия, Петропавловск-Камчатск
Зарегистрирован: 2010-03-22
Сообщения: 3292
Репутация: +  221  -
Профиль   Отправить e-mail  

проблема threading

Galvanize
не будут работать нормально и просто зависнут.
не правда. Сам запускал более 100 потоков (из академических целей). Да и в крайнем случае это уже вопрос не к Питону а к ОС.
Galvanize
Ко мне часто обращаются люди с просьбой написать всякие сканеры портов, которые смогут работать “ОЧЕНЬ БЫСТРО”, и на мой вопрос “насколько быстро” отвечают “ну, потоков в 500-1000”. Я говорю, что это невозможно, а они приводят мне какие-то доводы о том, что какой-то другой автор смог такое сделать. Я конечно же, сразу думаю, что человека обманули, т.к. скорее всего там нет параллельной работы сразу всех потоков, а стоит некий пул, из которого эти потоки по очереди запускаются.
вы не правы. У вас абсолютно нету тонкого понимания особенностей работы потоков и процессов. Тут уже говорили, но я по другому кратко изложу - потоки и процессы распараллеливают код, потоки удобны коммуникацией межу основным потоком и дочерними, но они не приспособлены к нагруженным действиям. Процессы это изолированный код , поэтому на них не распространяется GIL и как следствие они способны “держать” нагрузку.
К вопросу почему ЭТО возможно - узкое место в сетевых сканерах это операции потока ввода/вывода (простаивание в ожидании ответа) т.е. это не CPU высоконагруженные операции и как следствие GIL постоянно не переключается = производительность не падает.




_________________________________________________________________________________
полезный блог о python john16blog.blogspot.com

Офлайн

#7 Март 16, 2014 11:07:25

Galvanize
От:
Зарегистрирован: 2011-01-14
Сообщения: 67
Репутация: +  0  -
Профиль   Отправить e-mail  

проблема threading

JOHN_16
не правда. Сам запускал более 100 потоков (из академических целей). Да и в крайнем случае это уже вопрос не к Питону а к ОС.
Ну да, я тоже запускал. Но программа с большим кол-вом потоков не всегда завершается. Данные все собраны, но программа висит. Предполагаю, что они как-то блокируются во время работы и не завершаются. Возможно, что это из-за записи в файл, и блокировок на неё (RLock()).

JOHN_16
вы не правы. У вас абсолютно нету тонкого понимания особенностей работы потоков и процессов. Тут уже говорили, но я по другому кратко изложу - потоки и процессы распараллеливают код, потоки удобны коммуникацией межу основным потоком и дочерними, но они не приспособлены к нагруженным действиям. Процессы это изолированный код , поэтому на них не распространяется GIL и как следствие они способны “держать” нагрузку.
Тонкого понимания, разумеется, у меня нет. Я бы сюда не писал. О том, что потоки и процессы это способ распараллеливания это понятно, и то что процессы не подвержены gil я тоже знал. Но процессов должно быть по 2 на каждое ядро для комфортной работы, а это не совсем быстро.



Отредактировано Galvanize (Март 16, 2014 11:09:54)

Офлайн

#8 Март 16, 2014 11:46:50

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

проблема threading

Galvanize
Но процессов должно быть по 2 на каждое ядро для комфортной работы, а это не совсем быстро.
Чтобы вы этим хотели сказать? Комфортной работы ядер? Разработчика? Не совсем быстро по отношению к чему? Приведите 2 кода один быстрый а другой не совсем… Тогда можно будет и разбираться.

Если виснет тоже шлите код. Я тоже пробовал много тредов запускать, ничего не виснет. Это проблема в логике синхронизации.



Отредактировано doza_and (Март 16, 2014 11:50:14)

Офлайн

#9 Март 16, 2014 19:52:13

o7412369815963
От:
Зарегистрирован: 2009-06-17
Сообщения: 1986
Репутация: +  32  -
Профиль   Отправить e-mail  

проблема threading

Galvanize
будет ли лучше сделать асинхронный однопоточный сканер, вместо многопоточного?
Да.
Часто работает правило: есть много открытых соединений (в один момент времени)? - используй асинхронность.
Вот пример загрузчика на торнадо, там можете выставить 500, 1000, 2000 :)
Когда процесс будет упираться в ядро (100% нагрузки), то смысла увеличивать кол-во “потоков” нет и нужно распаралелливать на процессы.

Офлайн

#10 Март 17, 2014 03:31:13

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10016
Репутация: +  857  -
Профиль   Отправить e-mail  

проблема threading

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

def f(lst):
    lst.append(1)

из-за того что он не является потокобезопасным, GIL не даёт писать в него двум потокам сразу

#!/usr/bin/env python3
 
import timeit
import threading
 
def lf(lst):
    lst.append(1)
 
def f1():
    lst = []
    lf(lst)
    lf(lst)
 
def f2():
    lst = []
    lf(lst)
    t = threading.Thread(target=lf, args=(lst,))
    t.start()
    t.join()
 
def main():
    t1 = timeit.Timer('f1()', 'from __main__ import f1')
    t2 = timeit.Timer('f2()', 'from __main__ import f2')
 
    for t in t1, t2:
        print(t.repeat(3, 10000))
 
if __name__ == '__main__':
    main()

[guest@localhost py]$ ./thread_сmp.py 
[0.008367057002033107, 0.008361406002222793, 0.008245788998465287]
[2.177827506999165, 2.260574555999483, 2.182044921999477]
[guest@localhost py]$

habra. разбор от Бизли


add
даже вот так, снизив затраты на вызовы
#!/usr/bin/env python3
 
import timeit
import threading
 
def lf(lst):
    for i in range(10000):
        lst.append(i)
 
def f1():
    lst = []
    lf(lst)
    lf(lst)
 
def f2():
    lst = []
    lf(lst)
    t = threading.Thread(target=lf, args=(lst,))
    t.start()
    t.join()
 
def main():
    t1 = timeit.Timer('f1()', 'from __main__ import f1')
    t2 = timeit.Timer('f2()', 'from __main__ import f2')
 
    for t in t1, t2:
        print(t.repeat(3, 100))
 
if __name__ == '__main__':
    main()

[guest@localhost py]$ ./thread_сmp.py 
[0.31656129700058955, 0.3210945509999874, 0.32146822299910127]
[0.916342791999341, 0.9165670609982044, 0.8958057629970426]
[guest@localhost py]$



Отредактировано py.user.next (Март 17, 2014 12:03:09)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version