Lua 脚本中的 nanosleep() 调用暂停了 QT GUI 线程
我正在开发一个测试工具来从 PC 并行端口生成波形。该工具旨在生成计时精度为毫秒的任何波形模式,因此我使用 Lua 脚本来定义波形模式,当用户单击 [Start] 按钮时,GUI 启动新的 QThread 来运行脚本。
Lua 的以下三个函数被实现为 C++ 全局函数:
- pwrite:将数据写入并行端口。
- msleep:等待一定的毫秒(使用nanosleep()实现)
- print:覆盖Lua默认的打印函数,该函数会将消息附加到一个QTextEdit小部件。
当调用pwrite时,写入的数据存储在全局变量中,然后以20ms的间隔更新GUI,以更新GUI上的并口数据。 (这个20ms的间隔刷新不是一个好的设计,但我还没有弄清楚当数据改变时如何使用信号使GUI更新)。
目前该工具已经基本可以使用了。波形输出没有问题,但是并口数据更新有问题:
当Lua调用msleep时,GUI线程被停止,并口数据只有在msleep结束后才更新。
所以我的问题是:
如何实现 sleep 方法,使其不会阻止 GUI 线程更新?
如何实现pwrite,让GUI在写入数据改变时可以收到信号来更新并口数据?
如何
程序GUI如下链接:
相关代码:
/* common.cpp file */
int L_MSleep(lua_State* l)
{
int milisec=0;
struct timespec req={0, 0};
time_t sec;
milisec=luaL_optint(l,1,0); // obtain parameter
if (milisec==0)
return 0;
sec=(int)(milisec/1000);
milisec=milisec-(sec*1000);
req.tv_sec=sec;
req.tv_nsec=milisec*1000000L;
while(nanosleep(&req,&req)==-1)
continue;
return 1;
}
/* LuaRunner.cpp file */
LuaRunner::LuaRunner(QObject *parent) :
QThread(parent)
{
runlua = false;
}
void LuaRunner::run()
{
QString err = "";
runlua = true;
LUA_RunScript(this->ff, err);
runlua = false;
if(err != "")
{
emit errorMessage(err);
}
}
int LuaRunner::LUA_RunScript(QString ff, QString &err)
{
L = lua_open();
luaL_openlibs(L);
if (luaL_loadfile(L, ff.toAscii()) || lua_pcall(L, 0, 0, 0))
{
err = QString(lua_tostring(L, -1));
return -1;
}
lua_register(L, "ssleep", L_SSleep);
lua_register(L, "msleep", L_MSleep);
lua_register(L, "pwrite", L_PortWrite);
lua_register(L, "print", L_Log);
lua_getglobal(L, "dotest");
if (!lua_isfunction(L, -1))
{
err = QString("Test function(dotest) should be a function");
return -1;
}
if(lua_pcall(L, 0, 0, 0))
{
err = QString(lua_tostring(L, -1));
return -1;
}
lua_close(L);
return 0;
}
I am developing a test tool to generate waveform from PC parallel port. This tools is designed to generate any pattern of waveform with timing accuracy of ms, so I use Lua script to define the waveform pattern, the GUI start new QThread to run the script when user clicks [Start] button.
The following three functions for Lua are implemented as C++ global functions:
- pwrite: write data to parallel port.
- msleep: wait for certain ms (implemented using nanosleep())
- print: overwrite Lua default print function, this one will append message to one QTextEdit widget.
when pwrite is called, the written data is stored in global variable, then the GUI is updated with 20ms interval to update the parallel port data on the GUI. (this 20ms interval refresh is not a good design, but I haven't figure out how to use signal to make GUI update when data changed).
The tool is basically functional now. The waveform output has no problem, but the parallel port data updating has some problem:
When Lua call msleep, GUI thread is stopped, the parallel port data updates only after msleep ends.
So my questions are:
How to implement the sleep method so that it won't stop the GUI thread from updating?
How to implement the pwrite, so that the GUI can receive a signal to update the parallel port data when written data changed?
Program GUI as the link below:
The related code:
/* common.cpp file */
int L_MSleep(lua_State* l)
{
int milisec=0;
struct timespec req={0, 0};
time_t sec;
milisec=luaL_optint(l,1,0); // obtain parameter
if (milisec==0)
return 0;
sec=(int)(milisec/1000);
milisec=milisec-(sec*1000);
req.tv_sec=sec;
req.tv_nsec=milisec*1000000L;
while(nanosleep(&req,&req)==-1)
continue;
return 1;
}
/* LuaRunner.cpp file */
LuaRunner::LuaRunner(QObject *parent) :
QThread(parent)
{
runlua = false;
}
void LuaRunner::run()
{
QString err = "";
runlua = true;
LUA_RunScript(this->ff, err);
runlua = false;
if(err != "")
{
emit errorMessage(err);
}
}
int LuaRunner::LUA_RunScript(QString ff, QString &err)
{
L = lua_open();
luaL_openlibs(L);
if (luaL_loadfile(L, ff.toAscii()) || lua_pcall(L, 0, 0, 0))
{
err = QString(lua_tostring(L, -1));
return -1;
}
lua_register(L, "ssleep", L_SSleep);
lua_register(L, "msleep", L_MSleep);
lua_register(L, "pwrite", L_PortWrite);
lua_register(L, "print", L_Log);
lua_getglobal(L, "dotest");
if (!lua_isfunction(L, -1))
{
err = QString("Test function(dotest) should be a function");
return -1;
}
if(lua_pcall(L, 0, 0, 0))
{
err = QString(lua_tostring(L, -1));
return -1;
}
lua_close(L);
return 0;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您在专用线程中正确运行 Lua 脚本。这就是正确的做法——几乎。每次要运行脚本时,您都会重新启动线程。那是错误的。您还可以从 LUA 线程访问 GUI 线程中的数据,而无需任何同步。那不好。 Qt 以信号和槽之间的排队连接的形式为此提供了一种出色的机制。当信号槽调用传递线程边界时,参数将包装在 QEvent 中并异步传递到目标 QObject。在每个线程中,事件传递都是序列化的,因此您无需担心数据损坏等问题。
具体做法如下:
我将 UI 的实现留给您想象。请注意,这是未经测试的代码。据我所知,它可能会炸毁你的计算机。
You correctly run your Lua script in dedicated thread. That's the right way to do it -- almost. You are restarting the thread each time you want to run the script. That's wrong. You are also accessing the data in the GUI thread from the LUA thread without any sort of synchronization. That's not good. Qt provides an excellent mechanism for that in the form of queued connections between signals and slots. When signal-slot calls pass thread boundaries, the parameters get wrapped in a
QEvent
and get asynchronously delivered to the targetQObject
. Within each thread, the event delivery is serialized and thus you don't need to worry about data corruption etc.Here's how it should be done:
I leave the implementation of the UI to your imagination. Note that it's untested code. It may blow up your computer for all I know.
也许您应该使用 QT 的 msleep 因为它是由
QThread
Perhaps you should use QT's msleep since it is provided for use by
QThread