一、脚本语言的可抢占能力
根据脚本引擎核心的抢占能力,脚本引擎大概可分三类:
1. 第一类脚本,它的语句是细粒度的,而且如果它没有完成所有命令,就不能被宿主抢占(脚本不能被打断)。
例如Python,它提供的PyRun_SimpleString是不可抢占(除非它自己退出,或在脚本运行于一个后台线程内)
2. 第二类脚本,它实际上是宏命令集合(相当于描述或配置文件)。它的执行过程可以被宿主打断。
例如XML,它的执行(实际上是解析)速度很快。脚本既可以一次性地向宿主传递数据就马上退出,也可以以单步的形式在脚本解析和宿主执行之间轮流切换。
3. 第三类脚本,它们的虚拟机核心部分是可抢占的,即脚本自身可以暂停或挂起自己,通过退出虚拟机把控制权交还宿主。等待宿主完成某些事情后重新恢复脚本的执行。
例如Lua,允许使用一种叫“协程”的机制,并且提供一个API叫lua_resume,使Lua和宿主的执行流能有尽可能多的机会交错切换(详细可以参考这个链接(日文):http://marupeke296.com/LUA_No3_Coroutine.html)。由于可抢占的优势,Lua可以轻松处理事件驱动的系统。只要它有机会挂起自己,就能避免“程序无响应”的情况(前提是脚本执行的时间粒度足够小)。
二、如何让非抢占脚本引擎与消息循环共存
如上所述,脚本的可抢占能力与实时事件的处理能力是相关联的。如果脚本不需要花太多的时间(不处理鼠标事件),那么能否被抢占是无需考虑的。但万一脚本需要花很多时间(需要处理鼠标事件),而且脚本不可抢占,那么程序就可能困死在脚本中,没有时间处理窗口事件。
问题是:如果选择的脚本语言是单线程(同一时间只能执行一段脚本)且不可抢占(阻塞的),如何让它和实时系统(窗口系统)共存。
我认为解决方法有以下几种:
1. 多线程
由于脚本的执行是同步的,与异步的窗口事件处理不兼容,可以显式地创建一个后台线程,让脚本的执行独立于窗口系统,通过线程间的共享内存进行通信。需要考虑多线程的数据竞争问题。
2. 在脚本内处理窗口系统的消息循环
由于Windows的窗口处理实际上是多线程的,可以让主线程的消息循环在脚本内执行,使窗口事件的处理不被阻塞。虽然这种方法没有显式创建线程,但原理实际上和方法1相同。
3. 与可抢占的脚本语言混合使用
使用多于一种脚本引擎来操纵程序。需要考虑不同脚本状态机之间的数据共享问题。
4. 把一个脚本分拆成多个脚本
让窗口事件处理(如鼠标事件)的逻辑单独放在一个脚本文件。不过如果脚本状态机不是全局的,还需要留意状态数据的共享问题。
我觉得第2种办法是最优雅的,因为它不需要太复杂的机制。不过这么做的话,脚本既要处理逻辑,还要处理底层。
三、简单地在Python脚本中嵌入Win32消息循环
我尝试用第2种解决方法在Win32窗口程序内嵌入Python 2.2.2脚本(模仿一个日本游戏引擎KAVG的做法)。
大概写法如下(注意,这里忽略脚本的错误信息输出和窗口输入处理,而且用PyRun_SimpleString不太好,仅供参考)
脚本引擎部分:
#include <Python.h> #include <windows.h> #include "script.h" #include "mainframe.h" static PyObject *trace(PyObject *self, PyObject *args) { char* input; if (!PyArg_ParseTuple(args, "s", &input)) { return NULL; } OutputDebugString(input); OutputDebugString("\n"); return PyInt_FromLong(0); } static PyObject *foo(PyObject *self, PyObject *args) { return PyInt_FromLong(42L); } static PyObject *peekMsg(PyObject *self, PyObject *args) { return PyInt_FromLong(MainFrameMainLoop()); } void PyInit_SAVG(void) { PyObject *m; static PyMethodDef SAVG_methods[] = { //{"foo", (PyCFunction)foo, METH_NOARGS, "Return the meaning of everything."}, {"trace", (PyCFunction)trace, METH_VARARGS, "Output debug info for debugging."}, {"peekMsg", (PyCFunction)peekMsg, METH_NOARGS, "Window message loop"}, {NULL, NULL} }; PyImport_AddModule("SAVG"); m = Py_InitModule("SAVG", SAVG_methods); PyModule_AddStringConstant(m, "SAVG_VERSION", SAVG_VERSION); } static int script_init(void) { int ret; ret = PyRun_SimpleString( "import SAVG\n" "SAVG.trace(\"Script environment is initializing...\")\n" "SAVG.trace(\"SAVG_VERSION is %s\" % SAVG.SAVG_VERSION)\n" ); if(ret == -1) { OutputDebugString("error on script_init\n"); return 0; } return 1; } static void script_main(void) { int ret; ret = PyRun_SimpleString( "while 1:\n" " if SAVG.peekMsg():\n" " break\n" ); if(ret == -1) { OutputDebugString("error on script_main\n"); } } //NOTE:This function is in WinMain (Main Thread) int runScript(void) { Py_SetProgramName("SimpleScriptSystem"); Py_Initialize(); PyInit_SAVG(); if(script_init()) { script_main(); } Py_Finalize(); return 0; }
主窗口(部分代码):
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); MyRegisterClass(hInstance); if(!InitInstance(hInstance, nCmdShow)) { return FALSE; } //TODO: Using script engine to dispatch message, //or the main window will have no response!!! //As follow: // while(1) // { // if(MainFrameMainLoop()) // break; // } runScript(); //FIXME: //the return value of program should be : //(int) msg.wParam; return 0; } int MainFrameMainLoop(void) { MSG msg; if(!GetMessage(&msg, 0, 0, 0)) { return 1; } if(!TranslateAccelerator(msg.hwnd, NULL, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
原本执行GetMessage的循环被Python脚本接管了,所以即使script_main没有执行完,窗口系统也不会失去响应。
此时Python脚本不能直接处理输入事件,只能在peekMsg之后轮询输入缓冲(上面的代码没有实现此功能)。
四、总结
用类似Python的不可抢占脚本语言处理实时事件是可以的,关键是让脚本内的微观操作尽快完成,但不需要让整个脚本尽快完成。在山穷水尽的时候,还可以依靠操作系统的多线程能力。
对于win32消息循环,Python脚本可以通过C扩展来避免使用协程。
五、参考资料
1. Lua組み込み編:その3 コルーチンで状態遷移をLuaで制御
http://marupeke296.com/LUA_No3_Coroutine.html
2. Lua下实现抢占式多线程
http://blog.codingnow.com/2011/08/lua_52_multithreaded.html
3. 游戏引擎脚本系统(二)
http://www.blogjava.net/tianlinux/archive/2007/06/01/121434.html
相关推荐
10. **安全与道德问题**:使用抢号脚本需注意遵守华西医院的挂号规定,尊重网络公平原则,不进行恶意抢占或破坏系统的行为。同时,由于涉及到个人敏感信息,脚本应确保数据的安全传输和存储。 了解以上知识点后,你...
scramble4gpuscramble4gpu是用来在抢占显卡的脚本,主要是在实验室显卡资源紧张且自己亟需使用显卡的情况下,使用该脚本自动抢占一个或多个显卡。建议将scramble4gpu.py更改为train.py;防止被打...依赖numpytorch ...
在“DataCastle竞赛‘抢占C位,燃情世界杯’,秒杀脚本.zip”这个压缩包中,我们主要关注的是“世界杯程序”的实现,特别是其中的“秒杀脚本”。秒杀系统通常用于处理高并发场景,比如在世界杯期间,可能会有大量...
RouterOS是由MikroTik公司开发的一款强大的路由器操作系统,它提供了丰富的配置选项和脚本能力,适用于企业级网络环境。 HTB(Hierarchical Token Bucket)是一种流量整形工具,它基于Token Bucket算法,将不同...
在本压缩包“华为MSTP+VRRP脚本.rar”中,包含了相关的实验配置和脚本,这将帮助我们深入了解这两种协议的工作原理及配置方法。 **MSTP(多生成树协议)** 是STP(生成树协议)的一种增强版本,旨在解决传统的STP在...
3. **上A房和B房**:在射击游戏中,A房和B房可能指的是地图上的两个关键区域,如控制点或目标点,通过脚本可以实现快速、异常地到达这些区域,可能有利于战术布置或抢占先机。 4. **卡矿场**:这可能是游戏中的一个...
Melang是单线程中抢先调度协程的脚本语言。 仅在UNIX / Linux上受支持。 Melang已经支持MySQL 8.0,但是最新MySQL C客户端库是不稳定的。 因此,如果尝试连接无法访问的地址,即使程序可能不会崩溃,也会发生缓冲区...
- Makefile:用于编译和构建项目的脚本。 使用avr-simple-scheduler时,开发者需要注意以下几点: - 任务的执行时间:避免编写可能导致长时间阻塞的操作,如长时间的I/O操作或计算密集型任务。 - 资源管理:由于AVR...
该脚本是由shell语言编写完成,主要用于实现一键部署graylog-sidecar组件,其中包含...下载该脚本后可根据自身环境的服务器大小,来动态调整cgroup资源限制,使得该组件能以更优的方式运行。不会发生抢占系统资源现象。
《MetaTrader 5脚本——VoltyChannel_Stop详解与应用》 在金融市场交易的世界里,技术分析工具扮演着至关重要的角色。...掌握并熟练运用此脚本,无疑能够提升交易效率,助力交易者在复杂多变的市场中抢占先机。
其次,使用PHP的ticks机制也存在限制,即ticks指令只能在当前脚本范围内有效,而不能作用于require或include引入的脚本,这使得抢占式调度器的实现变得复杂。 Swoole团队考虑了多种方法来实现抢占式调度,最终选择...
2. **市场数据获取**:MT4平台提供了内置函数来获取实时的市场报价,如`SymbolInfoDouble(Symbol(), SYMBOL_BID)`和`SymbolInfoDouble(Symbol(), SYMBOL_ASK)`分别获取当前货币对的买价和卖价。 3. **价格比较与...
这款脚本的主要特点在于其可视化界面,使得操作更加直观易懂,同时也提供了强大的商品监控和自动化下单通知功能。 首先,我们要理解什么是“秒杀”。秒杀是电商平台为了促销而推出的一种限时限量销售模式,商品在短...
总之,"Intraday_Intensity_Open_Form"是一款旨在提升日内交易效率的MT5脚本,通过对市场强度和开盘形态的综合分析,为交易者提供决策支持。通过深入理解和有效利用这一工具,交易者可以在瞬息万变的市场中抢占先机...
另外,使用非官方的修改脚本也存在安全风险,可能导致电脑被植入恶意软件。 对于喜欢探索游戏可能性的玩家来说,理解CE脚本的工作原理和如何编写,也是一种技术学习的过程。CE本身是一个强大的内存调试工具,通过...
DPCs用于在高优先级的后台任务中执行非抢占式操作,而IOTIMER则是处理I/O事件的时间触发器。在内核安全软件仿XT中,对这些机制的监控能够防止恶意程序滥用它们进行恶意活动,如持久化、隐藏或者干扰系统服务。 SSDT...
在多维光通信/光处理和非线性/纳米光子器件/新型存储器件/计算与存储高效融合/智能存储等基础前沿研究中取得原创性成果,掌握自主知识产权,突破封锁,抢占先机实现弯道超越。 在能量光电子领域,面向我国能源结构...
1.2 **一体化开发**:提供统一的开发平台,支持SQL脚本编写、任务调度、数据质量监控等,确保数据处理过程的规范性和一致性。 1.3 **自动化运维**:内置完善的运维监控体系,实时跟踪数据流程,自动预警异常,降低...
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它以其事件驱动、非阻塞I/O模型而被广泛应用于构建高效、可扩展的网络应用,尤其是在实时应用领域。本文主要探讨了如何利用Node.js平台构建分布式实时应用,并...
4、因为各沙箱对全局环境有读权限,各沙箱可以共享全局环境,避免lua基础库和公共脚本的重复加载。5、在调度的过程中通过lua hook的方式实现任务的抢占式调度。Build && Testcd Cellular && make./testcase