c – 来自Lua脚本的nanosleep()调用暂停了QT GUI线程

我正在开发一种测试工具,用于从PC并行端口生成波形.此工具旨在生成任何波形图案,定时精度为ms,因此我使用Lua脚本定义波形图案,当用户单击[开始]按钮时,GUI启动新的QThread以运行脚本.

Lua的以下三个函数实现为C全局函数

> pwrite:将数据写入并行端口.
> msleep:等待某个ms(使用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))
    {
        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,-1));
        return -1;
    }

    lua_close(L);

    return 0;
}

解决方法

您正确地在专用线程中运行Lua脚本.这是正确的方法 – 差不多.每次要运行脚本时都要重新启动线程.那是错的.您还从LUA线程访问GUI线程中的数据,而不进行任何同步.这不好. Qt以信号和插槽之间的排队连接的形式提供了一种出色的机制.当signal-slot调用通过线程边界时,参数将被包装在QEvent中并异步传递给目标QObject.在每个线程中,事件传递是序列化的,因此您不必担心数据损坏等.

这是应该如何做的:

// LUAObject.h
#include <QObject>

class LUAObject : public QObject
{
   Q_OBJECT
public:
   LUAObject(QObject * parent = 0);
public slots:
   void setScript(const QString &);
   void runScript();
   void stop();

signals:
   void hasError(const QString &);
   void finished();
   void hasParallelData(int);
   void hasMessage(const QString &);

private:
   QString script;
   bool stop;
}

// LUAObject.cpp

// whatever Lua includes you need etc

LUAObject::LUAObject(QObject* p) : QObject(p)
{}

void LUAObject::stop() { stopped = true; }    

void LUAObject::setScript(const QString & scr)
{ script = scr; }

int L_PWrite(lua_State* l)
{
   int data = luaL_optint(l,-1);
   if (data != -1) {
      // access the parallel port HERE,NOT in the GUI thread!
      emit hasParallelData(luaL_optint(l,0));
   }
   return 0;
}

// returns a bool - true means we are stopped and should exit
int L_MSleep(lua_State* l)
{
   int ms = luaL_optint(l,-1);
   if (ms == -1) return 0;
   QApplication::processEvents(QEventLoop::WaitForMoreEvents,ms);
   lua_pushBoolean(l,stopped); // event processing would run the stop() slot call
   return 1;
}

int L_SSleep(lua_State* l)
{
   int secs = luaL_optint(l,-1);
   if (secs == -1) return 0;
   QApplication::processEvents(QEventLoop::WaitForMoreEvents,secs*1000);
   lua_pushBoolean(l,stopped); // event processing would run the stop() slot call
   return 1;
}

int L_Log(lua_State* l)
{
   const char * msg = luaL_optstring(l,0);
   if (!msg) return 0;
   emit hasMessage(msg);
   return 0;
}

class Lua // RAII
{
public:
   explicit Lua(lua_state * l) : L(l) {}
   ~Lua() { lua_close(L); }
   operator lua_state*() const { return L; }
private:
   lua_state * L;
   Q_disABLE_copY(LUA)
};

LUAObject::runScript()
{
   stopped = false;
   Lua L(lua_open());
   luaL_openlibs(L);

   if (luaL_loadbuffer(L,script.toAscii().constData(),script.length(),"script") || lua_pcall(L,0))
   {
       emit hasError(lua_tostring(L,-1));
       return;
   }

   lua_register(L,L_SSleep);
   lua_register(L,L_MSleep);
   lua_register(L,L_PWrite);
   lua_register(L,L_Log);

   lua_getglobal(L,"dotest");
   if (!lua_isfunction(L,-1))
   {
      emit hasError("Test function(dotest) should be a function");
      return;
   }

   if(lua_pcall(L,0))
   {
      emit hasError(lua_tostring(L,-1));
      return;
   }

   emit finished();
}

// main.cpp

#include <QApplication>
#include <QMetaMethod>
#include "LUAObject.h"

...

int main(int argc,char** argv)
{
   QApplication(argc,argv);

   MainWindow window;

   ...
   QThread thread;
   LUAObject lua;
   thread.start(QThread::TimeCriticalPriority);
   lua.movetoThread(&thread);

   ...

   // NOTE: you can ONLY connect to LUAObject slots,you CANNOT call them
   // directly since it runs in a separate thread!
   connect(&window,SIGNAL(startClicked()),&lua,SLOT(runScript());
   connect(&lua,SIGNAL(hasError(QString)),&window,SLOT(luaError(QString)));

   ...
   window.show();
   int rc = qApp->exec();
   QMetaObject::invokeMethod(&lua,SLOT(stop())); // cross-thread slot invocation
   thread.exit();
   thread.wait();
   return rc;
}

我将UI的实现留给您的想象力.请注意,它是未经测试的代码.它可能会让我知道的所有东西都爆炸.

相关文章

对象的传值与返回说起函数,就不免要谈谈函数的参数和返回值...
从实现装饰者模式中思考C++指针和引用的选择最近在看...
关于vtordisp知多少?我相信不少人看到这篇文章,多半是来自...
那些陌生的C++关键字学过程序语言的人相信对关键字并...
命令行下的树形打印最近在处理代码分析问题时,需要将代码的...
虚函数与虚继承寻踪封装、继承、多态是面向对象语言的三大特...