绿色线程和Python中的线程

59

根据维基百科所述:

绿色线程是一种模拟多线程环境的方式,不依赖于任何本地操作系统功能,并且它们在用户空间中管理而不是内核空间中,使它们能够在没有原生线程支持的环境中工作。

Python的线程使用pthreads(内核线程)实现,并且由于全局解释器锁(GIL)的存在,Python进程一次只运行一个线程。

[问题] 但是,在使用Green-threads(或称为greenlet或tasklet)时:

  1. GIL是否影响它们?是否可以同时运行多个greenlet?
  2. 使用greenlets或tasklets的缺点有哪些?
  3. 如果我使用greenlets,一个进程可以处理多少个greenlets?(我想知道这个因为在单个进程中,您可以打开线程直到在您的*ix系统中设置的ulimit(-s,-v))。

我需要一点见解,如果有人能分享他们的经验,或者指导我走向正确的道路,那将会很有帮助。


1
所有三个问题的答案都是“取决于greenlet实现”。 - millimoose
1
Stackless Python 涉及了许多这些概念。我建议获取一个版本并在官方网站上完成教程。它对你正在提出的问题有很多解释。 - yurisich
2个回答

42
您可以将greenlets视为协作式线程。这意味着在任何给定时刻,没有调度程序会抢占地在线程之间切换 - 相反,您的greenlets会在代码中指定的特定点自愿/显式地放弃对彼此的控制。
GIL是否会影响它们?是否可以同时运行多个greenlet?
一次只能运行一个代码路径 - 优点是您可以完全控制哪一个。
使用greenlets或tasklets的缺点是什么?
您需要更加小心 - 编写不良的greenlet将不会将控制权移交给其他greenlet。另一方面,由于您知道greenlet何时会上下文切换,因此您可能无需为共享数据结构创建锁定即可获得成功。
如果我使用greenlets,进程可以处理多少个?
(我想知道,因为在单个进程中,您可以在*ix系统中设置的umask限制内打开线程。)
使用常规线程,您拥有的线程越多,调度程序开销就越大。此外,常规线程仍具有相对较高的上下文切换开销。绿色子进程不具有与之相关的开销。从bottle documentation中可以了解到:

由于在线程之间切换和创建新线程涉及很高的开销,大多数服务器将其工作池的大小限制为相对较低的并发线程数。虽然与进程(forks)相比,线程是便宜的,但每个新连接都要创建它们仍然很昂贵。

gevent模块将绿色子进程添加到混合中。Greenlets的行为类似于传统线程,但创建成本非常低。基于gevent的服务器可以生成成千上万个绿色子进程(每个连接一个),几乎没有任何开销。阻塞单个绿色子进程不会影响服务器接受新请求的能力。并发连接数几乎无限。

如果您感兴趣,这里还有一些进一步的阅读材料:http://sdiehl.github.io/gevent-tutorial/

1
https://web.archive.org/web/20160304020253/www.devmusings.com/blog/2013/05/23/python-concurrency/ - Martin Konecny
1
感谢您将所有信息整理在一起,我认为有了这里提供的信息,人们可以快速前进。谢谢@Martin - Rahul Gautam
@MartinKonecny 这可能不是最好的地方来询问,但是,“一次只有一个代码路径在运行”这个说法对于所有用户线程(这与greenlets相同,对吗?)是否有效,还是仅适用于Python? - Vivek Anand
点赞!那么假设我有10个绿色线程在执行相同的两个SQLite表A和B之间的10个不同连接,您是说它们会按顺序运行? - PirateApp
它们将按顺序运行,是的。只要在每个查询之后都yield每个greenlet。 - Martin Konecny

6

我猜想你在谈论evenlet/gevent greenlets。

1) 只能同时运行一个greenlet。

2) 这是协作式多线程,意味着如果一个greenlet陷入无限循环,整个程序都会卡住。通常情况下,greenlets是在I/O期间或者显式调度的。

3) 它比线程需要更多的RAM,具体取决于可用内存的数量。


那么你的意思是,使用greenlet的唯一优点就是可以拥有比真实线程更多的“线程”。 - Rahul Gautam
2
我不确定,但我认为在绿色线程之间切换比在操作系统线程之间切换更快,因为它们更轻量级,但请不要引用我的话。 - rguillebert
绿色线程的成本与调用函数大致相同,而多线程需要上下文切换(在内存中保存整个线程状态,加载新线程的上下文直到循环结束)。这两种方法的开销不属于同一级别(进程甚至更糟)。 - Tim
@Tim 所有被切换的线程只包括程序计数器、处理器寄存器和堆栈指针。这比完整的内存地址空间要小得多。这是线程概念存在的主要原因之一,而不是所有东西都是进程。 - gbtimmon
@gbtimmon,你说得完全正确。我并不打算涉及这个细节层面。实际上,有多个因素相互作用,尽管线程具有绿色线程所没有的一些特点(它们可以在多核、超线程等技术的支持下同时存在),但我们可以希望高频率的绿色线程令牌交接比共享同一物理资源的线程性能稍微好一些... - Tim
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接