从 gevent 文档:
greenlet 都运行在同一个操作系统线程中,并被协同调度。
那么是否还需要使用gevent lock原语或者gevent.Queue来避免单个线程中多个greenlet之间的竞争条件?一个演示这种竞争条件的例子将不胜感激。根据我自己的理解,这些同步原语似乎只是在 greentlets 之间切换执行流程的一种方式。
是的,一般来说,在gevent中使用锁和同步构造还是很有必要的。
线程和 gevent 下的锁定和同步构造,例如 RLock、Semaphore 和 Queue,通过保护对关键数据或关键代码部分的访问(实际上,假装这样的部分或片段)来确保程序状态在内部保持一致的数据全部自行运行)。
greenlet 和线程之间的区别在于,虽然理论上线程上下文更改可能在您完全无法控制的任何时间发生,但 greenlet 上下文更改只能在特定定义的时刻发生,因此理论上,如果您在编程时非常小心并且完全控制关键部分或数据的使用方式,您可以完全避免切换并消除对锁的需要。有时这很容易做到,有时则不然,这取决于程序。在 gevent 中,当 IO,time.sleep()等都可能导致切换时,如果代码复杂度很高,则很难完全确定不会有切换,因此关于同步和锁定的标准规则是最好的。
这是一个例子。假设我们想将一些消息(结构化数据)写入文件或类文件对象。让我们想象一下,这些消息以流式方式放在一起,一次一个块,但接收者需要能够将消息一起阅读——将两个不同消息的块散布在一起会导致乱码。
def generate_data(chunks):
# This task generates the data that makes up a message
# in chunks.
# Imagine that each chunk takes some time to generate.
# Maybe we're pulling data from a database.
for chunk in chunks:
yield chunk
def worker_one(file):
file.write("begin message")
for chunk in generate_data('abcde'):
file.write(chunk)
file.write("end message")
def worker_two(file):
file.write("begin message")
for chunk in generate_data('123456'):
file.write(chunk)
file.write("end message")
output_file = get_output_file()
workers = [gevent.spawn(worker_one, output_file),
gevent.spawn(worken_two, output_file)]
gevent.joinall(workers)
Run Code Online (Sandbox Code Playgroud)
如果get_output_file简单地返回open('/some/file'),这将工作正常:使用常规file对象不与 gevent 循环配合,因此每个工作程序将运行到完成而不会让步,并且消息将完好无损。
但是,如果它返回socket.create_connection(("some.host", 80)).makefile(),这将失败并且消息将被分割。一个工作人员对套接字的每次写入都可能让 greenlet 产生收益而另一个 greenlet 运行,从而导致数据乱码。
如果generate_data更复杂,可能通过 gevent 套接字与服务器或数据库通信,那么即使我们正在写入文件,消息也可能会出现乱码,因为在生成数据的过程中,greenlets 发生了切换。
这是为什么共享状态(在这种情况下是套接字)可能需要使用同步构造来保护的示例。
| 归档时间: |
|
| 查看次数: |
842 次 |
| 最近记录: |