昨天调试项目时,突然发现如下消息:
QObject::connect: Cannot queue arguments of type 'ERROR_LEVEL'
(Make sure 'ERROR_LEVEL' is registered using qRegisterMetaType().)
其中ERROR_LEVEL只是我定义的枚举类型即enum ERROR_LEVEL。然后在Qt的信号-槽函数的参数中使用了这个枚举型,在发送信号时就出现了上述警告。上面警告的大概意思是信号队列中无法使用ERROR_LEVEL类型,要使用qRegisterMetaType()注册该类型后方可使用。
通常使用的connect,实际上最后一个参数使用的是Qt::AutoConnection类型:
bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )
Qt支持6种连接方式,其中3中最主要:
- Qt::DirectConnection(直连方式)
当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。(信号与槽函数关系类似于函数调用,同步执行)
- Qt::QueuedConnection(排队方式)
当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信,异步执行)
- Qt::AutoConnection(自动方式)
Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。
我的项目中的确跨线程使用了ERROR_LEVEL为参数类型的信号,因此使用的应当是排队方式的信号-槽机制,出现“队列中无法使用ERROR_LEVEL类型”的警告信息就可以理解了。放狗搜了一圈,有篇文章提供了个这样的解决方案:
connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),
this,SLOT(sendRes(QUuid,QByteArray,bool)));
改为:
connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),
this,SLOT(sendRes(QUuid,QByteArray,bool)), Qt::DirectConnection);
这样做的确能使警告信息消失,因为Qt官方文档写了:
With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes.
即使用排队方式的信号-槽机制,Qt的元对象系统(meta-object system)必须知道信号传递的参数类型。这里手动改为直连方式,Qt的元对象系统就不必知道参数类型了,于是警告信息消失。但这样做是不安全的,见Qt官方文档:
Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.
因此,咱还是老老实实地用qRegisterMetaType()注册类型吧,见代码:
头文件:
/*! \file errorlevel.h*/#ifndef ERRORLEVEL_H#define ERRORLEVEL_H/*! \enum ERROR_LEVEL* 错误等级的定义。通常用来在传递错误消息时标记错误的等级。*/enum ERROR_LEVEL{NORMAL, /*!< 普通错误,通常不需要处理,可以记录或者显示错误消息。*/CRITICAL /*!< 严重错误,需要紧急处理,如果没有妥善处理,可能导致后续操作失败。*/};#endif // ERRORLEVEL_H
cpp文件:
元类型注册方法受益于这篇文章。/*! \file errorlevel.cpp*/#include "errorlevel.h"/*! 模板函数显式实例化,注册ERROR_LEVEL到元对象系统。这样才可以在信号-槽* 队列里使用ERROR_LEVEL类型参数。*/int i = qRegisterMetaType<ERROR_LEVEL>("ERROR_LEVEL");
继removeDatabase()警告、C++ 传指针还是传引用的困惑、Qt槽可否使用默认参数、诡异的ESC关闭事件以及Qt元类型注册门等一系列波折后,我的corpus creator终于释出了第一个release版!上来就进行各种界面测试,然后来了个8000语料库创建。在贴log前,我先磨叽会儿。corpus creator使用的是经典的生产者-消费者多线程模型,其中共享100个单位的环形缓冲区,使用类似于管程的同步机制。它采用键绑定机制(把数据源绑定到语料库中存放语料的表的键上),从而提供了良好的弹性。各窗口各操作可重入……
来贴一下热乎的运行时log,是从GUI的状态框中复制粘贴出来的:
01:49:56 >> 正在搜索语料文件,请稍候…
01:49:57 >> 语料文件搜索完毕,共找到 8000 个语料文件!
01:49:57 >> 语料文件处理中…
01:49:57 >> ICTCLAS 分词器初始化成功!
01:49:58 >> 提交数据失败!
01:49:59 >> 提交数据失败!
01:50:00 >> 提交数据失败!
==>中间略去N个失败……
01:52:09 >> 提交数据失败!
01:52:10 >> 提交数据失败!
01:52:12 >> 语料文件处理完毕,等待写入完毕…
01:52:12 >> 处理了 7808 个语料文件!
01:52:12 >> 语料写入完毕!
提交失败主要是因为该语料分词 + 句子成分标注后过长,超出了键类型长度。然后用SQL检查了创建结果,的却是入库7808个语料,并且抽查语料均分词正常!
corpus creator运行时,内存消耗在140M左右波动。采用本机未经优化MySql数据库,创建时传输率450+KB/S。速度还是很令人满意的,8000的语料库2、3分钟就创建完毕了。先高兴地睡觉去~
