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

我正在开发一个测试工具来从PC并行端口生成波形。 这个工具被设计用于生成任意波形模式,其定时精度为ms,所以我使用Lua脚本来定义波形模式,当用户单击[Start]按钮时,GUI启动新的QThread来运行脚本。

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

  • 写入:将数据写入并行端口。
  • msleep:等待某个ms(使用nanosleep()实现)
  • 打印:覆盖Lua的默认打印功能,这一个将附加消息到一个QTextEdit小部件。
  • 当调用pwrite时,写入的数据存储在全局变量中,然后以20ms的间隔更新GUI以更新GUI上的并行端口数据。 (这种20ms的间隔刷新并不是一个好的设计,但是当数据改变时,我还没有弄清楚如何使用信号来更新GUI)。

    该工具现在基本上可以使用。 波形输出没有问题,但并行端口数据更新有一些问题:

    当Lua调用msleep时,GUI线程停止,并行端口数据仅在msleep结束后更新。

    所以我的问题是:

  • 如何实现睡眠方法,以便它不会停止更新GUI线程?

  • 如何实现pwrite,以便在写入数据更改时,GUI可以接收信号以更新并行端口数据?

  • 程序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;
    }
    

    你在专用线程中正确运行你的Lua脚本。 这是正确的做法 - 差不多。 每次要运行脚本时都要重新启动线程。 这是错误的。 您还可以从LUA线程访问GUI线程中的数据,而无需进行任何类型的同步。 这不好。 Qt为信号和插槽之间的排队连接形式提供了一个很好的机制。 当信号槽调用传递线程边界时,参数将被封装在一个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, -1);
       if (data != -1) {
          // access the parallel port HERE, NOT in the GUI thread!
          emit hasParallelData(luaL_optint(l, 1, 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, -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, -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, 1, 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, 0, 0))
       {
           emit hasError(lua_tostring(L, -1));
           return;
       }
    
       lua_register(L, "ssleep", L_SSleep);
       lua_register(L, "msleep", L_MSleep);
       lua_register(L, "pwrite", L_PWrite);
       lua_register(L, "print", 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, 0, 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的实现留给了你的想象。 请注意,它是未经测试的代码。 它可能会炸毁我所知的所有电脑。


    也许你应该使用QT的msleep,因为它提供给QThread使用

    链接地址: http://www.djcxy.com/p/55605.html

    上一篇: nanosleep() call from Lua script paused QT GUI thread

    下一篇: Result from querying Spotify API differs from identical query in web client