是否可以在CPython中以编程方式构造堆栈(一个或多个堆栈帧)并在任意代码点开始执行?想象一下以下场景:
您有一个工作流引擎,其中工作流可以在Python中编写脚本,其中包含一些构造(例如分支,等待/加入),这些构造是对工作流引擎的调用.
阻塞调用(例如等待或连接)在具有某种持久性后备存储的事件调度引擎中设置侦听器条件.
您有一个工作流脚本,它调用引擎中的等待条件,等待稍后将发出信号的某些条件.这将在事件调度引擎中设置侦听器.
工作流脚本的状态,包括程序计数器(或等效状态)的相关堆栈帧是持久的 - 因为等待条件可能在几天或几个月后发生.
在此期间,工作流引擎可能会停止并重新启动,这意味着必须能够以编程方式存储和重新构建工作流脚本的上下文.
事件调度引擎触发等待条件获取的事件.
工作流引擎读取序列化状态并堆栈并使用堆栈重构线程.然后在调用等待服务的点继续执行.
问题
可以使用未修改的Python解释器吗?更好的是,有人能指出一些可能涵盖此类事物的文档,或者以编程方式构造堆栈帧并在代码块中间某处开始执行的代码示例吗?
编辑:为了澄清'未修改的python解释器',我不介意使用C API(在PyThreadState中是否有足够的信息来执行此操作?)但我不想去探讨Python解释器的内部并且有建立一个修改过的.
更新:从一些初步调查,可以获得执行上下文PyThreadState_Get().这将返回PyThreadState(定义在pystate.h)中的线程状态,该状态具有对堆栈帧的引用frame.堆栈帧保存在一个结构类型中PyFrameObject,定义为frameobject.h. PyFrameObject有一个字段f_lasti(bobince的道具),它有一个程序计数器,表示为从代码块开头的偏移量.
这最后是一个好消息,因为它意味着只要您保留实际编译的代码块,您就应该能够根据需要为尽可能多的堆栈帧重建本地,并重新启动代码.我想说这意味着它在理论上是可能的,而不必制作一个修改过的python interpereter,虽然这意味着代码仍然可能会非常繁琐并与特定版本的解释器紧密耦合.
剩下的三个问题是:
事务状态和'saga'回滚,这可能是通过一种用于构建O/R映射器的元类黑客来完成的.我确实曾经构建过一次原型,因此我对如何实现这一目标有了一个很好的了解.
强大地序列化事务状态和任意本地.这可以通过读取__locals__(可从堆栈帧获得)并以编程方式构建对pickle的调用来完成.但是,我不知道在那里可能有什么,如果有的话.
版本控制和升级工作流程.这有点棘手,因为系统没有为工作流节点提供任何符号锚.我们所拥有的只是锚点为了做到这一点,人们必须确定所有入口点的偏移并将它们映射到新版本.可能手动完成,但我怀疑自动化很难.如果您想支持此功能,这可能是最大的障碍.
更新2: ( PyCodeObject)code.h有地址的列表(f_lasti) - >在行号的映射PyCodeObject.co_lnotab(如果错在这里指正).这可以用于促进迁移过程以将工作流更新到新版本,因为冻结的指令指针可以映射到新脚本中的适当位置,根据行号完成.仍然相当混乱,但更有希望.
更新3:我认为答案可能是Stackless Python. 您可以暂停任务并将其序列化.我还没有弄清楚这是否也适用于堆栈.
所以我刚刚在Python全局解释器锁(GIL)http://blip.tv/file/2232410上看完了这个演讲.
它的要点是GIL对于单核系统来说是一个非常好的设计(Python实际上将线程处理/调度留给了操作系统).但是,这可能严重地适应多核系统,最终导致IO密集型线程被CPU密集型线程严重阻塞,上下文切换费用,ctrl-C问题[*]等等.
因此,由于GIL限制我们基本上在一个CPU上执行Python程序,我的想法是为什么不接受这个并简单地在Linux上使用taskset来设置程序与系统上某个核心/ cpu的亲和性(特别是在某种情况下)在多核系统上运行的多个Python应用程序)?
所以最终我的问题是:有没有人尝试在Linux上使用带有Python应用程序的任务集(特别是在Linux系统上运行多个应用程序,以便多个核心可以与一个或两个绑定到特定核心的Python应用程序一起使用),如果是这样的话是结果?值得做吗?是否会使某些工作负载更糟糕?我打算这样做并进行测试(基本上看看程序是否需要花费更多或更少的时间来运行),但我很乐意听取其他人的经验.
另外:David Beazley(在链接视频中发表演讲的人)指出,一些C/C++扩展手动释放GIL锁,如果这些扩展针对多核进行优化(即科学或数字数据分析/等),那么而不是为数字运算获得多核的好处,扩展将被有效地削弱,因为它仅限于单个核心(因此可能显着降低程序速度).另一方面,如果您没有使用此类扩展
我没有使用多处理模块的原因是(在这种情况下)程序的一部分是严重的网络I/O绑定(HTTP请求),所以有一个工作线程池是一个很好的方式从一个盒子挤出性能一个线程触发一个HTTP请求,然后因为它在I/O上等待就放弃了GIL而另一个线程可以做到这一点,所以程序的一部分可以轻松地运行100多个线程而不会伤害CPU太多让我实际使用可用的网络带宽.对于无堆栈的Python/etc,我对重写程序或替换我的Python堆栈并不过分感兴趣(可用性也是一个问题).
[*]只有主线程可以接收信号,所以如果你发送一个ctrl-C,Python解释器基本上试图让主线程运行,这样它就可以处理信号,但因为它不能直接控制运行哪个线程(它留给操作系统)它基本上告诉操作系统继续切换线程,直到它最终命中主线程(如果你不幸,可能需要一段时间).
我计划在RTOS平台上实现一个小规模的数据采集系统.(在QNX或RT-Linux系统上.)
据我所知,这些作业是使用C/C++执行的,以充分利用系统.然而,我很想知道并且想要学习一些经验丰富的人的意见,然后我盲目地进入编码行动,是否可行且更明智地用Python编写所有内容(从低级仪器通过闪亮的图形用户界面连接).如果没有,将设计的时序关键部分与"C"混合,或者用C编写所有内容,甚至不用一行Python代码.
或者至少使用Python包装C代码以便更容易地访问系统.
你建议我以哪种方式工作?如果你指出一些类似的设计案例和进一步的阅读材料,我会很高兴的.
谢谢
注1:强调QNX的原因是我们已经有一个基于QNX 4.25的数据采集系统(M300)用于我们的大气测量实验.这是一个专有系统,我们无法访问它的内部.进一步研究QNX可能对我们有利,因为6.4有免费的学术许可选项,Python 2.5附带,以及最近的GCC版本.我从未测试过RT-Linux系统,不知道它在稳定性和效率方面与QNX有多大可比性,但我知道Python系统的所有成员和非Python工具(如Google Earth)的新系统可以在大多数情况下开箱即用.
我想在我的.NET应用程序中嵌入一个Python解释器.我当然知道IronPython,但我对PyPy特别感兴趣,因为它支持无堆栈和微线程.
但是,虽然可以针对CLI构建PyPy,但它看起来只是为您提供了一个独立的Python解释器和python.exe.我无法找到任何可以构建实际嵌入.NET宿主应用程序内容的文档.
有没有办法使用(无堆栈)PyPy从.NET应用程序运行Python脚本,并允许这些脚本与主机应用程序提供的CLR对象进行交互?
新版PyPy附带集成的Stackless.据我所知,捆绑的Stackless与2001年的Stackless起源不同.所以主要是带调度程序的绿色线程框架.
Greenlet是Stackless的旋转,它提供Stackless绿色线程功能作为扩展模块.
有没有使用"原生"的任何利益无堆栈从PyPy比PyPy + greenlet +一些调度(如:GEVENT)?或问题是我不能使用PyPy的那些类型的扩展?更具体一点:我知道PyPy有自己的greenlet实现(基于continulet).但我很好奇在PyPy中将外部greenlet与gevent和内部greenlet连接起来的可能性.
PyPy是否附带了一个用于Stackless的异步IO库而不是标准的?
我知道stackless本身和python的其他异步轻线程扩展(eventlet,gevent,twisted ......).因此,我不是在寻找它们之间的差异,而是通过无堆叠构建而形成的pypy的优势.
我一直在尝试在C++应用程序中嵌入不同的脚本语言,目前我正在尝试Stackless Python 3.1.我已经尝试了几个教程和示例,我可以找到的很少,尝试从应用程序运行一个简单的脚本.
Py_Initialize();
FILE* PythonScriptFile = fopen("Python Scripts/Test.py", "r");
if(PythonScriptFile)
{
PyRun_SimpleFile(PythonScriptFile, "Python Scripts/Test.py");
fclose(PythonScriptFile);
}
Py_Finalize();
Run Code Online (Sandbox Code Playgroud)
出于某些奇怪的原因,运行此代码会导致访问冲突:
PyRun_SimpleFile(PythonScriptFile, "Python Scripts/Test.py");
Run Code Online (Sandbox Code Playgroud)
我在网上搜索了类似问题的其他人,发现只有一个.他们唯一的解决方案是只在老版本的Python中才有可能的解决方法:创建一个python文件对象并将该python文件对象返回FILE*到PyRun_SimpleFile.但是,这样的函数调用不可用,Python 3.1 API从文件描述符创建文件对象并返回文件描述符,但该PyRun_SimpleFile函数仍然需要FILE*.
我不知道如何从文件中运行任何脚本,没有手动将整个文件加载到内存中并将其作为一个巨大的字符串运行,当然不是一个实用的解决方案.
是什么赋予了?如果API有内部错误,我该如何完成此任务?
更新:我已经设法从源代码构建Stackless Python 3.1,尽管使用了相同的C运行时库,但崩溃仍然完全没有变化.我的项目和Stackless Python 3.1源都是使用Visual Studio 2010的C++编译器和C运行时构建的.我不再对可能解决这个问题的问题有所了解,只是修改Python以使用文件名而不是FILE*.另一个可怕的解决方法.
Stackless python允许您序列化一个任务(酸洗),以便以后执行,不需要在同一台机器上:http: //www.stackless.com/wiki/Pickling
我的问题是stackless python是否提供任何类型的IPC,中间件,服务代理或DDS技术来在进程和/或机器之间移动这些pickle任务?我们真的需要在这里使用套接字吗?
他们有一个很好的渠道概念:http: //www.stackless.com/wiki/Pickling
如果渠道跨越机器工作,那么这将是非常棒的,您可以简单地在网络上向服务代理注册渠道.实质上,允许您将任务移动到位于不同计算机上的不同无堆栈python服务.
我注意到了两种"消息传递"的方法.一个我见过Erlang使用,另一个来自Stackless Python.根据我的理解,这里的区别
Erlang样式 - 消息被发送并排队到接收进程的邮箱中.从那里它们以FIFO为基础被移除.一旦第一个进程发送消息,它就可以继续.
Python样式 - 进程A队列最多发送到进程B.B当前正在执行其他一些操作,因此A被冻结,直到B准备好接收.一旦B打开读取通道,A发送数据,然后它们都继续.
现在我看到Erlang方法的优点是你没有任何被阻止的进程.如果B永远无法接收,A仍然可以继续.但是我注意到在我编写的一些程序中,由于消息的流入量大于流出量,因此Erlang消息框可能会充满数百(或数千)个消息.
现在我还没有用任何框架/语言编写大型程序,所以我想知道你的经历是这样的,如果这是我应该担心的事情.
是的,我知道这是抽象的,但我也在寻找相当抽象的答案.
我正在开发一款回合制休闲MMORPG游戏服务器.
处理网络,多线程,定时器,服务器间通信,主游戏循环等的低级引擎(不是我们编写的)是由C++编写的.高级游戏逻辑由Python编写.
我的问题是关于我们游戏中的数据模型设计.
首先,我们只是尝试在客户端登录时将播放器的所有数据加载到RAM和共享数据缓存服务器中,并安排定时器将数据定期刷新到数据缓存服务器中,数据缓存服务器将数据保存到数据库中.
但我们发现这种方法存在一些问题
1)需要立即保存或检查某些数据,例如任务进度,等级,物品和货币收益等.
2)根据游戏逻辑,有时我们需要查询一些离线玩家的数据.
3)一些全球游戏世界数据需要在不同的游戏实例之间共享,这些游戏实例可能在不同的主机上运行,或者在同一主机上的不同进程上运行.这是我们需要数据缓存服务器位于游戏逻辑服务器和数据库之间的主要原因.
4)玩家需要在游戏实例之间自由切换.
以下是我们过去遇到的困难:
1)所有数据访问操作都应该是异步的,以避免网络I/O阻塞主游戏逻辑线程.我们必须向数据库或缓存服务器发送消息,然后在回调函数中处理数据回复消息并继续进行游戏逻辑.编写一些需要与db多次交谈并且游戏逻辑分散在许多回调函数中的中等复杂游戏逻辑很快变得痛苦,这使得难以理解和维护.
2)ad-hoc数据缓存服务器使事情变得更加复杂,我们难以维护数据一致性并有效地更新/加载/刷新数据.
3)游戏中的数据查询效率低且繁琐,游戏逻辑需要查询许多信息,如库存,项目信息,化身状态等.还需要一些交易机制,例如,如果一步失败,整个操作应该回滚.我们尝试在RAM中设计一个好的数据模型系统,构建大量复杂的索引以简化大量的信息查询,增加事务支持等.我很快意识到我们正在构建的是一个内存数据库系统,我们正在重新发明轮子. ..
最后我转向无堆栈python,我们删除了缓存服务器.所有数据都保存在数据库中.游戏逻辑服务器直接查询数据库 使用无堆栈python的微任务和通道,我们可以以同步的方式编写游戏逻辑.它更容易编写和理解,生产力大大提高.
实际上,底层数据库访问也是异步的:一个客户端tasklet向另一个专用DB I/O工作线程发出请求,并且在一个通道上阻止了tasklet,但是整个主游戏逻辑没有被阻塞,其他客户端的tasklet将被安排并自由奔跑.当DB数据回复时,被阻止的tasklet将被唤醒并继续在'断点'上运行(继续?).
有了上面的设计,我有一些问题:
1)数据库访问比以前的缓存解决方案更频繁,数据库是否可以支持高频率的查询/更新操作?在不久的将来是否需要一些成熟的缓存解决方案,如redis,memcached?
2)我的设计有任何严重的缺陷吗?你们能给我一些更好的建议吗,特别是游戏中的数据管理模式.
任何建议将不胜感激,谢谢.