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

基本上读完了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("__greenlet_ts_tracekey");
	ts_event_switch = PyString_I
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值