python协程的实现(greenlet源码分析)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

基本上读完了greenlet的源代码,代码不多,就2000行C语言的代码,其中有一部分栈寄存器的修改的代码是由汇编实现的。。。

一句话来说明greenlet的实现原理:通过栈的复制切换来实现不同协程之间的切换。。。

那么接下里来具体的来看看greenlet的代码到底是怎么实现的。。。


好了,先来看看greenlet对象对应的C语言结构体:

/**
States:
  stack_stop == NULL && stack_start == NULL:  did not start yet
  stack_stop != NULL && stack_start == NULL:  already finished
  stack_stop != NULL && stack_start != NULL:  active

**/

//greenlet对象最终对应的数据的C结构体,这里可以理解为python对象的属性
typedef struct _greenlet {
	PyObject_HEAD
	char* stack_start;   //栈的顶部  将这里弄成null,标示已经结束了
	char* stack_stop;    //栈的底部
	char* stack_copy;     //栈保存到的内存地址
	intptr_t stack_saved;   //栈保存在外面的大小
	struct _greenlet* stack_prev;  //栈之间的上下层关系
	struct _greenlet* parent;    //父对象
	PyObject* run_info;   //其实也就是run对象
	struct _frame* top_frame;   //这里可以理解为主要是控制python程序计数器
	int recursion_depth;   //栈深度
	PyObject* weakreflist;
	PyObject* exc_type;
	PyObject* exc_value;
	PyObject* exc_traceback;
	PyObject* dict;
} PyGreenlet;

这里stack_start,stack_stop就是用于分别指向当前这个协程的栈的顶部与栈的底部,而且可以通过这几个标志位的值来判断当前这个greenlet对象的状态。。。

另外这里可以看到parent指针,用这个来构成greenlet对象的层次关系。。。

接下来来看看与python对象之间对应关系:

//这个是定义的暴露给python的greenlet对象,它以greenlet.h里面定义的_greenlet位基础,然后指定了方法,创建方法等
PyTypeObject PyGreenlet_Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"greenlet.greenlet",			/* tp_name */
	sizeof(PyGreenlet),			/* tp_basicsize */  //指定对象的大小,其实也就是结构体的大小
	0,					/* tp_itemsize */
	/* methods */
	(destructor)green_dealloc,		/* tp_dealloc */
	0,					/* tp_print */
	0,					/* tp_getattr */
	0,					/* tp_setattr */
	0,					/* tp_compare */
	0,					/* tp_repr */
	&green_as_number,			/* tp_as _number*/
	0,					/* tp_as _sequence*/
	0,					/* tp_as _mapping*/
	0, 					/* tp_hash */
	0,					/* tp_call */
	0,					/* tp_str */
	0,					/* tp_getattro */
	0,					/* tp_setattro */
	0,					/* tp_as_buffer*/
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | GREENLET_GC_FLAGS,	/* tp_flags */
	"greenlet(run=None, parent=None) -> greenlet\n\n"
	"Creates a new greenlet object (without running it).\n\n"
	" - *run* -- The callable to invoke.\n"
	" - *parent* -- The parent greenlet. The default is the current "
	"greenlet.",                            /* tp_doc */
	(traverseproc)GREENLET_tp_traverse,	/* tp_traverse */
	(inquiry)GREENLET_tp_clear,		/* tp_clear */
	0,					/* tp_richcompare */
	offsetof(PyGreenlet, weakreflist),	/* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	green_methods,				/* tp_methods */  //对象方法的定义
	0,					/* tp_members */
	green_getsets,				/* tp_getset */ //属相方法
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	offsetof(PyGreenlet, dict),		/* tp_dictoffset */   //这个对象的dict的偏移
	(initproc)green_init,			/* tp_init */  //初始化  这里主要是设置run以及parent
	GREENLET_tp_alloc,			/* tp_alloc */   //内存分配,其实也就是分配sizeof(PyGreenlet)的大小
	green_new,				/* tp_new */  //构造函数   这里会将parent默认的设置为全局的greenlet
	GREENLET_tp_free,			/* tp_free */      //释放   
	(inquiry)GREENLET_tp_is_gc,		/* tp_is_gc */   //gc
};

这个里面构造以及初始化函数都提供了,那么先来看看构造函数吧,当在python中执行环境中创建greenlet对象的时候,将会先调用green_new方法,接着调用green_init方法。。。

//创建一个greenlet对象,这个可以理解为greenlet的构造函数
//这里会先将parent默认指向当前环境所属的greenlet对象
//如果在greenlet对象的构造函数中带有parent对象,那么待会在init中会设置
static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	PyObject* o;
	if (!STATE_OK)
		return NULL;
	
	//先用基本对象来构造
	o = PyBaseObject_Type.tp_new(type, ts_empty_tuple, ts_empty_dict);
	if (o != NULL) {   //这里默认将parent设置为ts_current,也就是在哪一个greenlet环境里面创建的,那么新创建的greenlet的parent就是所属的环境greenlet
		Py_INCREF(ts_current);
		((PyGreenlet*) o)->parent = ts_current;
	}
	return o;
}

这个代码很好理解吧,先构造父类,接着设置当前greenlet的默认parent,这里可以看到将parent默认的设置为当前环境所属的greenlet对象,也就是说假如我们在A中创建另外一个greenlet B,而且构造函数中没有传递parent参数,这个并没有关系,因为默认就已经将B的parent设置为A了...

好了,接下来来看看初始化函数:

//初始化greenlet对象,这个可以理解为在构造之后,将会调用这个方法来进行初始化,主要是检查设置run,以及设置parent
//这里可能没有parent参数,不过也没有关系,因为在构造的时候就先设置为创建环境的greenlet
static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs)
{
	PyObject *run = NULL;
	PyObject* nparent = NULL;
	//看当前这两个东西是否存在
	static char *kwlist[] = {"run", "parent", 0};
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:green", kwlist,
					 &run, &nparent))
		return -1;

	if (run != NULL) {  ///这里run对象是必须有的
		//设置run对象
		if (green_setrun(self, run, NULL))
			return -1;
	}
	//这里在初始化的时候可以没有parent
	if (nparent != NULL && nparent != Py_None) 
		//如果没有指定parent对象,那么将跟对象设置,在这个里面将会进行parent链是否存在环的情况进行判断
		return green_setparent(self, nparent, NULL);
	return 0;
}

这里初始化函数主要是设置构造的时候传递进来的执行函数,也就是run对象,然后就如果有传递parent的话,将会将这个greenlet对象的parent设置为传递进来的greenlet对象。。。。。

接下来来看看greenlet对象对应的一些方法的定义:

//这个就是暴露给greenlet对象的的方法了
static PyMethodDef green_methods[] = {
    {"switch", (PyCFunction)green_switch,  //switch方法的定义
     METH_VARARGS | METH_KEYWORDS, green_switch_doc},
    {"throw",  (PyCFunction)green_throw,  METH_VARARGS, green_throw_doc},
    {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL},
    {NULL,     NULL}		/* sentinel */
};

这里就可以看到最为重要的switch方法其实对应的green_switch方法,。。嗯,那么接下来就来对照着如下python代码来分析greenlet吧:

from greenlet import greenlet

def test1(name):
	print name
	gr2.switch()
	print "over"

def test2():
	print "test-2"
	return "test2-fjsfjs"

	
gr1 = greenlet(test1)
gr2 = greenlet(test2)
value = gr1.switch("fjsfjs")
print value

首先,得要先来看看greenlet模块的初始化:

PyMODINIT_FUNC
initgreenlet(void)
#endif
{
	PyObject* m = NULL;
	char** p = NULL;
	PyObject *c_api_object;
	static void *_PyGreenlet_API[PyGreenlet_API_pointers];

	GREENLET_NOINLINE_INIT();

#if PY_MAJOR_VERSION >= 3
	m = PyModule_Create(&greenlet_module_def);
#else
	m = Py_InitModule("greenlet", GreenMethods);   //初始化greenlet模块,这里其实定义的方法比较的少,getcurrent啥的
#endif
	if (m == NULL)
	{
		INITERROR;
	}

	//为模块添加版本信息
	if (PyModule_AddStringConstant(m, "__version__", GREENLET_VERSION) < 0)
	{
		INITERROR;
	}

#if PY_MAJOR_VERSION >= 3
	ts_curkey = PyUnicode_InternFromString("__greenlet_ts_curkey");
	ts_delkey = PyUnicode_InternFromString("__greenlet_ts_delkey");
#if GREENLET_USE_TRACING
	ts_tracekey = PyUnicode_InternFromString("__greenlet_ts_tracekey");
	ts_event_switch = PyUnicode_InternFromString("switch");
	ts_event_throw = PyUnicode_InternFromString("throw");
#endif
#else
	ts_curkey = PyString_InternFromString("__greenlet_ts_curkey");
	ts_delkey = PyString_InternFromString("__greenlet_ts_delkey");
#if GREENLET_USE_TRACING
	ts_tracekey = PyString_InternFromString("__green
使用React.ts创建一个密码生成器的简单示例 使用密码生成器工具创建随机密码。PC 浏览器体验 该Demo旨在于练习React.ts函数组件写法。Github项目地址 React TypeScript —— Function Components 为元素(::before/::after)绑定点击事件 React如何正确定义对象数组 在React中设置复选框`check`属性 三目运算符实现React动态绑定class和style 阅读详情

相关推荐

python 64式: 第44式、协程源码分析

目标: 弄清楚协程eventlet的源码以及具体如何实现并发的原理。 期间包含对greenlet,epoll的介绍。 内容目录如下: 1 eventlet简述 2 greenlet 3 epoll 4 源码分析 5 eventlet使用 6 总结 1 eventlet简述 1.1 eventlet基础 eventlet:协程(绿色线程),一个线程内的伪并发方式。通过互相渡让cpu实现并发。 原理...

qingyuanluofeng的专栏 579

Python greenlet.error: cannot switch to a different thread

debug模式下会对线程的切换造成混乱,解决方案: 第一种: 禁用debug模式即可 第二种:在主函数中加入monkey patch if __name__ == '__main__': from gevent import monkey monkey.patch_all() ...

ChaseNKSky的博客 1万+

Klipper源码分析-reactor.py

reactor 是 Klippy 中的一个模块,它负责管理事件的注册、调度和处理。它充当了事件驱动的核心引擎,驱动整个 Klippy 系统的运行。在 Klippy 中,reactor提供了一个统一的接口,使各个模块能够注册自己的回调函数以响应特定的事件。它使用事件循环的方式,不断地检查事件的状态并触发相应的回调函数。不过在学习reactor模块之前需要先学习一下python的select。

我的专栏 1351

使用grpc遇到greenlet.error: cannot switch to a different thread解决方法

发生问题场景: grpc服务端配置有一个使用eventlet.db_pool.DatabaseConnector创建的数据库连接池还有其他基本的条件语句,在使用客户端调用服务端的函数执行数据库操作时,几乎66%的概率在服务端发生阻塞,首先数据库是没问题的。重启客户端再重新调用相同的函数,服务端会报 greenlet.error: cannot switch to a different thread 的错误。 尝试解决方法: 官方:在grpc的github的issues里找到这个问题https://g

wuliwawa的博客 5210

python3 pycharm 断点调试 报错 greenlet.error: cannot switch to a different thread

错误信息 greenlet.error: cannot switch to a different thread 解决办法 setting->build->python debugger->gevent compatible(勾选)

whatday的专栏 6914

Python3:gevent:greenlet.error: cannot switch to a different thread

2020-08-01 22:06:12:034678: ---------------------------------------- 2020-08-01 22:06:50:875038: Exception happened during processing of request from ('127.0.0.1', 55207) 2020-08-01 22:06:50:875038: Traceback (most recent call last): 2020-08-01 22:06:50:8.

shunzi2016的博客 2172

python协程实现greenlet源码分析)

基本上读完了greenlet的源代码,代码不多,就2000行C语言的代码,其中有一部分栈寄存器的修改的代码是由汇编实现的。。。 一句话来说明greenlet实现原理:通过栈的复制切换来实现不同协程之间的切换。。。 那么接下里来具体的来看看greenlet的代码到底是怎么实现的。。。 好了,先来看看greenlet对象对应的C语言结构体: [cpp] view plai

permike的专栏 3608

greenlet的C语言实现,greenlet上下文切换的原理

greenletPython众多协程实现技术中的一种,eventlet是基于greenlet实现的。而eventlet和libev又是gevent的核心。greenlet的上下文切换清晰易懂,可以结合IO事件循环构建出一些高效的事件处理逻辑。不同于yield类型的上下文切换,greenlet的上下文切换从表现形式上看更纯粹,可以直接switch到另一个greenlet,不用管目标greenlet...

weixin_39912303的博客 331

如何为老旧Mac安装新系统:OpenCore Legacy Patcher完整配置指南

OpenCore Legacy Patcher是一个强大的Python项目,它通过Acidanthera的OpenCorePkg和Lilu技术,让不再受苹果官方支持的Mac设备能够运行macOS Big Sur及更新版本。这个工具为2007年以来的老旧Mac设备注入了新的活力,让你能够体验最新的macOS系统功能。 ## 1. 项目概述与价值定位 OpenCore Legacy Patcher

gitblog_00041的博客 558

python greenlet背景介绍与实现机制

最近开始研究Python的并行开发技术,包括多线程,多进程,协程等。逐步整理了网上的一些资料,今天整理一下greenlet相关的资料。  并发处理的技术背景       并行化处理目前很受重视, 因为在很多时候,并行计算能大大的提高系统吞吐量,尤其在现在多核多处理器的时代, 所以像lisp这种古老的语言又被人们重新拿了起来, 函数式编程也越来越流行。 介绍一个python的并行处理的一个库

西涛offbye-移动全栈技术博客 5177

Greenlet理解要点

Greenlet是给python使用的协程,evenlet就是使用的这个库。greenlet真正实现协程之间的切换。python协程实现greenlet源码分析)这篇博文非常精彩的讲解了greenlet。整个代码一共就两千来行,因为涉及到上下文切换,读起来还是有点困难的。本文主要讲讲理解greenlet的要点。A. 数据结构/** States: stack...

weixin_34111819的博客 205

gevent 学习笔记 —— greenlet源码剖析1

greenlet篇中只是简单讲述了一下greenlet原理,不知道有没有人对源码感兴趣的,不过我还是比较感兴趣的。对于技术我一直都觉得『知其然,知其所以然』才是最好的,不希望自己只停留在知道用,而不知道为什么这样用。 一篇可能介绍不完,所以可能需要分几部分来介绍。当我写到这的时候突然发现自己是不是写跑题了,本来是写greenlet源码分析的,结果变成了怎么去写Python扩展了。不过不管怎样,

zlklove1234的专栏 375

switch函数——Gevent源码分析

在gevent的源码中,经常能看到switch函数。而不同的类中的switch函数有不同的用法 1. greenlet的switch函数 这里面的greenletgreenlet库中的greenlet类,它是gevent中的Greenlet类的基类(Greenlet类是在greenlet类的基础之上的扩展) 在这个基类里面,它的switch函数的作用是:==切换到这个greenlet协程中,并...

weixin_33893473的博客 213

openstack基础_eventlet

greenlet源码流程解析,eventlet使用

RingoShen的博客 2507

python黑魔法asyncio_使用 Python 进行并发编程:我为什么不喜欢 Gevent

Python的发展历史中,有过一些失败的修复CPython的缺陷和提高性能的尝试,比如消除GIL、Stackless(一个微线程扩展,避免传统线程所带来的性能与复杂度问题)、psyco (被PyPy代替)、 Unladen Swallow 。当然也有少数成功的,比如PyPy。协程在开始表达我的观点之前,我们先来了解下Coroutine。Coroutine 也就是 corporate routin...

weixin_39913422的博客 145

Python并发编程eventlet

1 多进程、多线程和协程 python并发编程之多进程、多线程、异步和协程:http://www.cnblogs.com/tyomcat/p/5486827.html 1.1 总结 1)多进程能够利用多核优势,但是进程间通信比较麻烦,另外,进程数目的增加会使性能下降,进程切换的成本较高。程序流程复杂度相对I/O多路复用要低。 2)I/O多路复用是在一个进程内部处理多个逻辑流程,不用进行

翟海飞 1523

转:[gevent源码分析] 深度分析gevent运行流程

[gevent源码分析] 深度分析gevent运行流程 http://blog.csdn.net/yueguanghaidao/article/details/24281751 一直对gevent运行流程比较模糊,最近看源码略有所得,不敢独享,故分享之。 gevent是一个高性能网络库,底层是libevent,1.0版本之后是libev,核心是greenlet。gevent和eventlet...

weixin_30381793的博客 120

sleep函数——Gevent源码分析

gevent是一个异步I/O框架,当遇到I/O操作的时候,会自动切换任务,从而能异步地完成I/O操作 但是在测试的情况下,可以使用sleep函数来让gevent进行任务切换。示例如下: import gevent def test(id): print('Test %s is running...' % id) gevent.sleep(0) print('Test %s...

weixin_34037515的博客 438
上一篇: python协程入门(greenlet)
下一篇: Gevent的协程实现原理
fjs_cloud
博客等级 码龄15年 248粉丝 192原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值