Greenlet Vs. Потоки
Я новичок в gevents и greenlets. Я нашел хорошую документацию о том, как работать с ними, но никто не дал мне обоснования того, как и когда я должен использовать зелья!
- На что они действительно хороши?
- Можно ли использовать их на прокси-сервере или нет?
- Почему не потоки?
Что я не уверен в том, как они могут предоставить нам concurrency, если они в основном являются совместными программами.
Ответы
Ответ 1
Зелень обеспечивает concurrency, но не parallelism. concurrency - это когда код может работать независимо от другого кода. Parallelism - одновременное выполнение одновременного кода. Parallelism особенно полезен, когда в пользовательском пространстве требуется много работы, и это типично тяжелый процессор. concurrency полезен для устранения проблем, позволяя планировать и настраивать различные детали параллельно.
Greenlets действительно сияют в сетевом программировании, где взаимодействие с одним сокетом может происходить независимо от взаимодействия с другими сокетами. Это классический пример concurrency. Поскольку каждая зелень работает в своем собственном контексте, вы можете продолжать использовать синхронные API без потоковой передачи. Это хорошо, потому что потоки очень дороги с точки зрения виртуальной памяти и издержек ядра, поэтому concurrency, которую вы можете достичь с помощью потоков, значительно меньше. Кроме того, потоки в Python являются более дорогими и более ограниченными, чем обычно, благодаря GIL. Альтернативами concurrency обычно являются проекты типа Twisted, libevent, libuv, node.js и т.д., Где весь ваш код имеет один и тот же контекст выполнения и регистрирует обработчики событий.
Это отличная идея использовать зеленые (с соответствующей поддержкой сети, например, через gevent) для написания прокси-сервера, так как обработка запросов может выполняться независимо и должна быть написана как таковая.
Зелень предоставляет concurrency по причинам, которые я дал ранее. concurrency не parallelism. Сокрывая регистрацию событий и выполняя планирование для вас по вызовам, которые обычно блокируют текущий поток, проекты, такие как gevent, раскрывают этот concurrency, не требуя изменения асинхронного API и значительно меньшую стоимость вашей системы.
Ответ 2
Принимая @Max ответ и добавляя некоторую значимость к нему для масштабирования, вы можете увидеть разницу. Я достиг этого, изменив URL-адреса для заполнения следующим образом:
URLS_base = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
URLS = []
for _ in range(10000):
for url in URLS_base:
URLS.append(url)
Мне пришлось отказаться от версии с несколькими процессорами, поскольку она упала до того, как у меня было 500; но при 10000 итераций:
Using gevent it took: 3.756914
-----------
Using multi-threading it took: 15.797028
Итак, вы можете видеть, что в I/O есть существенная разница с использованием gevent
Ответ 3
Это достаточно интересно анализировать.
Вот код для сравнения производительности зеленых и многопроцессорных пулов по сравнению с многопоточными:
import gevent
from gevent import socket as gsock
import socket as sock
from multiprocessing import Pool
from threading import Thread
from datetime import datetime
class IpGetter(Thread):
def __init__(self, domain):
Thread.__init__(self)
self.domain = domain
def run(self):
self.ip = sock.gethostbyname(self.domain)
if __name__ == "__main__":
URLS = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
t1 = datetime.now()
jobs = [gevent.spawn(gsock.gethostbyname, url) for url in URLS]
gevent.joinall(jobs, timeout=2)
t2 = datetime.now()
print "Using gevent it took: %s" % (t2-t1).total_seconds()
print "-----------"
t1 = datetime.now()
pool = Pool(len(URLS))
results = pool.map(sock.gethostbyname, URLS)
t2 = datetime.now()
pool.close()
print "Using multiprocessing it took: %s" % (t2-t1).total_seconds()
print "-----------"
t1 = datetime.now()
threads = []
for url in URLS:
t = IpGetter(url)
t.start()
threads.append(t)
for t in threads:
t.join()
t2 = datetime.now()
print "Using multi-threading it took: %s" % (t2-t1).total_seconds()
вот результаты:
Using gevent it took: 0.083758
-----------
Using multiprocessing it took: 0.023633
-----------
Using multi-threading it took: 0.008327
Я думаю, что greenlet утверждает, что он не связан GIL в отличие от библиотеки многопоточности. Более того, документ Greenlet говорит, что он предназначен для сетевых операций. Для интенсивной работы с сетью потоковая коммутация прекрасна, и вы можете видеть, что многопоточный подход выполняется довольно быстро.
Также он всегда предпочтет использовать официальные библиотеки python; Я попытался установить greenlet на windows и столкнулся с проблемой зависимости dll, поэтому я провел этот тест на linux vm.
Alway попытается написать код с надеждой, что он работает на любой машине.