QT 进程,线程,多线程,线程池的理解:
QT 进程和线程理解:
- 一个应用就是一个进程,一个进程中包含一个线程或多个线程(称为单线程应用和多线程应用)。
- 线程必须存在于应用中,即线程必须依赖进程而不能单独存在。
- 进程在计算机内存中有单独的内存空间,但线程没有,因此线程必须用进程的内存空间。
多线程的实现:
目前常用多线程的实现是: 使用QThread 重写run来实现多线程,或者使用信号和槽的方式实现多线程。
线程锁,互斥量,信号量,线程同步:
要保证某资源在多线程中是安全,可靠顺序化,就要使用线程锁。目前使用线程锁的方式有两种互斥量和信号量。
互斥量QMutex
QMutex 目的是保护一个对象,数据结构或者代码段,同一时间只有一个线程可以访问它。
QMutex 的主要作用是保护第一次访问到资源线程的安全,主要用来初始化资源。
//简单举例:
void DebugInfo() //多个线程同时调用该函数时,输出打印就会乱掉。
{
qDebug("abc");
qDebug("def");
}
QMutex mutex; //多个线程共用一个mutex.
void DebugInfo1() //添加互斥量,多个线程调用这个方法时输出会顺序化,abcdef
{
mutex.lock();
qDebug("abc");
qDebug("def");
mutex.unlock();
}
//构造函数:
QMutex::QMutex ( bool recursive = FALSE ) // 创建QMutex 对象,当recursive为false 则构造普通互斥量,一个线程中只能缩影该互斥量一次。如果recursive为true 则一个线程中可以锁多次,等所有数量unlock后,才被解锁。
//析构函数:
QMutex::~QMutex ()
//锁定互斥量
void QMutex::lock ()
//判断互斥量是否被锁定,已锁定返回true,否则返回假。
bool QMutex::locked ()
//试图锁定互斥量:如果锁得到返回真,否则返回假。
bool QMutex::tryLock ()
//解锁互斥量:对不同线程中锁的互斥量进行解锁将返回错误。
void QMutex::unlock ()
参考资料:QMutex 使用介绍
信号量QSemaphore
信号量主要用于使线程执行顺序化,但同一时间可以有多个线程访问。
最大的作用就是可以在多线程同时访问同一资源的时候使其顺序访问。
QSemaphore ( int n = 0 )//建立对象时可以给它n个资源
~QSemaphore ()
void acquire ( int n = 1 )// 这个操作一次减少n个资源,如果现有资源不到n个就会阻塞
int available () const //返回当前可用的QSemaphore资源个数
void release ( int n = 1 )//这个操作一次增加n个资源
bool tryAcquire ( int n = 1 )//类似于acquire,但是申请不到n个资源时不会阻塞会立即返回
bool tryAcquire ( int n, int timeout )
创建QSemaphore 对象时,n的默认值是0, 所以要想acquire() 成功就必须先有执行release. 所以通常使用该信号量等待某些程序执行返回,否则一直阻塞。
线程池和多线程:
- 线程池是在程序运行开始,创建好的n个线程,并且这n个线程挂起等待任务的到来。而多线程是在任务到来得时候进行创建,然后执行任务。
- 线程池中的线程执行完之后不会回收线程,会继续将线程放在等待队列中;多线程程序在每次任务完成之后会回收该线程。
- 由于线程池中线程是创建好的,所以在效率上相对于多线程会高很多。
- 线程池也在高并发的情况下有着较好的性能;不容易挂掉。多线程在创建线程数较多的情况下,很容易挂掉。
参考资料:深入理解线程,进程,多线程,线程池
1.Qobject 类说明:
参考网址:Qobject 相关介绍
- QObject 类介绍:
QObject 类是所有Qt类的基类,是QT 对象模型的核心。
QObject 可以处理event事件,可以实现信号槽(无缝通信)。可以实现因此可以如下使用:
class QT_Test : public QObject
{
Q_OBJECT // 对于所有实现信号,槽或者属性的QObject 对象来说,该宏都是必须的。 即使不用信号槽也推荐每个QObject 的子类使用这个宏,可以避免一些奇怪行为。
public:
private:
};
- QObject 类特点:
- 线程亲和性:当一个QObject 对象接受到一个信号或者event时,相应的槽函数或事件处理器会在该对象所在的线程中执行,通常该对象所在线程为创建该对象时的线程。
- 没有拷贝构造函数和赋值运算符: 因此需要QObject 子类作为值的地方,需要使用QObject 指针来代替。 如不能讲QObject 对象作为值存储到容器类中。
QByteArray:
QByteArray 是C语言中char*的升级版本,一般用来存储数组。存储元素的最后自动补全\0.
构造函数:
// 构造空对象, 里边没有数据
QByteArray::QByteArray();
// 将data中的size个字符进行构造, 得到一个字节数组对象
// 如果 size==-1 函数内部自动计算字符串长度, 计算方式为: strlen(data)
QByteArray::QByteArray(const char *data, int size = -1);
// 构造一个长度为size个字节, 并且每个字节值都为ch的字节数组
QByteArray::QByteArray(int size, char ch);
数据操作:
// 在尾部追加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::append(const QByteArray &ba);
void QByteArray::push_back(const QByteArray &other);
// 头部添加数据
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::prepend(const QByteArray &ba);
void QByteArray::push_front(const QByteArray &other);
// 插入数据, 将ba插入到数组第 i 个字节的位置(从0开始)
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::insert(int i, const QByteArray &ba);
// 删除数据
// 从大字符串中删除len个字符, 从第pos个字符的位置开始删除
QByteArray &QByteArray::remove(int pos, int len);
// 从字符数组的尾部删除 n 个字节
void QByteArray::chop(int n);
// 从字节数组的 pos 位置将数组截断 (前边部分留下, 后边部分被删除)
void QByteArray::truncate(int pos);
// 将对象中的数据清空, 使其为null
void QByteArray::clear();
// 字符串替换
// 将字节数组中的 子字符串 before 替换为 after
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::replace(const QByteArray &before, const QByteArray &after);
字符串查找和判断:
//判断是否为空字符串
bool QByteArray::isEmpty();
// 判断字节数组中是否包含子字符串 ba, 包含返回true, 否则返回false
bool QByteArray::contains(const QByteArray &ba) const;
bool QByteArray::contains(const char *ba) const;
// 判断字节数组中是否包含子字符 ch, 包含返回true, 否则返回false
bool QByteArray::contains(char ch) const;
// 判断字节数组是否以字符串 ba 开始, 是返回true, 不是返回false
bool QByteArray::startsWith(const QByteArray &ba) const;
bool QByteArray::startsWith(const char *ba) const;
// 判断字节数组是否以字符 ch 开始, 是返回true, 不是返回false
bool QByteArray::startsWith(char ch) const;
// 判断字节数组是否以字符串 ba 结尾, 是返回true, 不是返回false
bool QByteArray::endsWith(const QByteArray &ba) const;
bool QByteArray::endsWith(const char *ba) const;
// 判断字节数组是否以字符 ch 结尾, 是返回true, 不是返回false
bool QByteArray::endsWith(char ch) const;
遍历:
// 使用迭代器
iterator QByteArray::begin();
iterator QByteArray::end();
// 使用数组的方式进行遍历
// i的取值范围 0 <= i < size()
char QByteArray::at(int i) const;
char QByteArray::operator[](int i) const;
查看字节数:
// 返回字节数组对象中字符的个数
int QByteArray::length() const;
int QByteArray::size() const;
int QByteArray::count() const;
// 返回字节数组对象中 子字符串ba 出现的次数
int QByteArray::count(const QByteArray &ba) const;
int QByteArray::count(const char *ba) const;
// 返回字节数组对象中 字符串ch 出现的次数
int QByteArray::count(char ch) const;
类型转换:
// 将QByteArray类型的字符串 转换为 char* 类型
char *QByteArray::data();
const char *QByteArray::data() const;
// int, short, long, float, double -> QByteArray
// 其他重载的同名函数可参考Qt帮助文档, 此处略
QByteArray &QByteArray::setNum(int n, int base = 10);
QByteArray &QByteArray::setNum(short n, int base = 10);
QByteArray &QByteArray::setNum(qlonglong n, int base = 10);
QByteArray &QByteArray::setNum(float n, char f = 'g', int prec = 6);
QByteArray &QByteArray::setNum(double n, char f = 'g', int prec = 6);
[static] QByteArray QByteArray::number(int n, int base = 10);
[static] QByteArray QByteArray::number(qlonglong n, int base = 10);
[static] QByteArray QByteArray::number(double n, char f = 'g', int prec = 6);
// QByteArray -> int, short, long, float, double
int QByteArray::toInt(bool *ok = Q_NULLPTR, int base = 10) const;
short QByteArray::toShort(bool *ok = Q_NULLPTR, int base = 10) const;
long QByteArray::toLong(bool *ok = Q_NULLPTR, int base = 10) const;
float QByteArray::toFloat(bool *ok = Q_NULLPTR) const;
double QByteArray::toDouble(bool *ok = Q_NULLPTR) const;
// std::string -> QByteArray
[static] QByteArray QByteArray::fromStdString(const std::string &str);
// QByteArray -> std::string
std::string QByteArray::toStdString() const;
// 所有字符转换为大写
QByteArray QByteArray::toUpper() const;
// 所有字符转换为小写
QByteArray QByteArray::toLower() const;
2. QMap 使用详解:
QMap 仅有键和键值,作为一个容器,它只能使两个数据产生对应关系。
QMap<QString, int> map; // 创建Qmap 实例, 键类型为QString 类型,键值为int 类型
map["chinese"]=100; //插入数据方式1
map.insert("english",99); //插入数据方式2
map.remove("chinese"); //移除数据
QMapIteraor<QString, int> iterator(map); // 使用迭代器遍历元素
while(iterator.hasNext()){
iterator.next();
qDebug() << iterator.key() << ":" << iterator.value();
map.value("english"); //由键查找键值
map.key(100); //由键值超找键
map.insert("english", 120); //通常一个键对应一个键值,这里可以用来修改键值
bool isok= map.contains("english"); //判断是否包含某个键
QList<QString> allkeys = map.keys(); //获取所有键
qDebug()<<allkeys;
QList<int> allValues = map.values(); //获取所有键值
qDebug()<<allValues;
}
/* 使用QMultiMap类来实例化一个QMap对象 */
QMultiMap<QString, QString> multiMap; //QMultiMap 是一个键对应多个键值
multiMap.insert("People", "Name");
multiMap.insert("People", "Age");
multiMap.insert("People", "Height");
multiMap.insert("People", "Weight");
qDebug() << multiMap;
/* 从打印结果可以看出multiMap仍为一个QMap对象 */
参考网址:Qmap 使用详解
QList 类使用介绍:
QList 是目前常用的容器类,在内部QList 使用数组来实现。
使用举例:
#include <QCoreApplication>
#include <QList>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 定义QList变量
QList<QString> list;
// 插入项目
list << "aa" << "bb" << "cc";
// 查询和重新定义元素
if(list[1] == "bb")
{
list[1] = "ab";
}
// 替换第3个元素
list.replace(2, "bc"); // 将“cc”换为“bc”
// 打印元素
qDebug() << "the list is: ";
for(int i=0; i<list.size(); ++i){
qDebug() << list.at(i); // 现在列表为aa ab bc
}
// 在列表尾部添加元素
list.append("dd");
// 在列表头部添加元素
list.prepend("mm");
// 从列表中删除第3个项目,并获取它
QString str = list.takeAt(2);
// 打印刚才删除的元素
qDebug() << "at(2) item is: " << str;
// 打印列表
qDebug() << "the list is: ";
for(int i=0; i<list.size(); ++i)
{
qDebug() << list.at(i); // 现在列表为mm aa bc dd
}
// 在位置2插入项目
list.insert(2, "mm");
// 交换项目1和项目3
list.swap(1,3);
// 打印列表
qDebug() << "the list is: ";
for(int i=0; i<list.size(); ++i)
{
qDebug() << list.at(i); // 现在列表为mm bc mm aa dd
}
// 列表中是否包含“mm”
qDebug() << "contains 'mm' ?" << list.contains("mm");
// 包含“mm”的个数
qDebug() << "the 'mm' count: " << list.count("mm");
// 第一个“mm”的位置,默认从位置0开始往前查找,返回第一个匹配的项目的位置
qDebug() << "the first 'mm' index: " << list.indexOf("mm");
// 第二个“mm”的位置,我们指定从位置1开始往前查找
qDebug() << "the second 'mm' index: " << list.indexOf("mm", 2);
return a.exec();
}
参考网址:QList 使用方法
Qt 容器:QStringList 类使用介绍:
QString 继承自QList 由Qstring 类型构成的List 列表。可以确认是否包含某QString 或filter 过滤包含的子串。
参考网址:QStringList
3. QScopedPointer 类使用介绍:
QScopedPointer 类似C++ 中的智能指针std:unique_ptr. QScopedPointer在其生命期结束后会自动删除它所指的对象。即出了作用域,指针就会被自动删除。防止内存泄露。
常见用法如下:
class MyClass
{
public:
private:
QScopedPointer<MyPrivateClass> privatePtr; // QScopedPointer to forward declared class
}
QSharedPointer:
QSharedPointer 是共享指针,类似QScopedPoinrer 都包装了new操作从堆上分配动态对象,与QScopedPointer 不同的是QsharedPointer 可以自由拷贝和赋值,在任意地方共享,通常用作容器元素。
QShredPointer 对内存资源进行引用计数,即三个QsharedPointer 对象可以指向同一块内存,当计数下降为0时会自动释放相关内存。
QSharedPointer 是线程安全的,但QSharedPointer 所指向的资源不一定是线程安全,因此多个线程同时修改QSharedPointer 指向的数据时,需要考虑加锁。
参考资料:QSharedPointer
QT 信号和槽函数:
一个对象的信号连接另外一个对象的槽函数。
信号的参数列表会送给槽处理函数,实际槽处理函数的参数可以<= 信号参数列表个数。
Connect
参考资料:信号和槽的介绍
参考资料:信号和槽的连接方式介绍
s+ code 目前使用两种方式如下:
connect(object1, SIGNAL(signal1()),this,SLOT(slot()),QT::QueuedConnection) //异步调用
connect(object1, SIGNAL(signal1()),this,SLOT(slot()),QT::DirectConnection) // 立即调用
qRegisterMetaType 的使用介绍:
当信号被放到队列中时,它的参数会被一起放到队列中去,Qt 需要construct, destruct,copy 这些对象,因此自定义的类型想使用signal/slot 传递的话,就需要使用qRefisterMetaType 来注册。
使用方法:
- 头文件包含#include < QMetaType >
- 在类定义完成后,加入声明Q_DECLARE_METATYPE(TextAndNumber);
- 在使用的类的构造函数中注册这种类型:qRegisterMetaType< TextAndNumber > (“TextAndNumber”);
举例说明
//mytest.h
#include<QMetaType>
typedef enum
{
S_INIT=0,
S_START=1,
S_STOP=2
}MYSTATE;
Q_DECLARE_METATYPE(MYSTATE);
class mytest:public QObject
{
Q_OBJECT
public:
signal:
void stateChanged(const string s1, MYSTATE st);
private:
mytest();
~mytest();
};
//mytest.cpp
mytest::mytest()
{
qRegisterMetaType<MYSTATE>("MYSTATE");
}
参考资料:qRegisterMetaType 介绍
Q_DECLARE_PRIVATE与Q_DECLARE_PUBLIC 使用介绍
通过两个宏,可以直接使用q 或者d 指针指向对应的类。具体参考资料:Q_DECLARE_PRIVATE & Q_DECLARE_PUBLIC 介绍
参考代码如下:
//TestControl.h
class TestControl :public QObject
{
Q_OBJECT
public:
void TestFunc1();
int TestFunc2();
protected:
QScopedPointer<TestControlPrivate> d_ptr; //声明d_ptr 指针, 也可以放到私有部分。
private:
Q_DECLARE_PRIVATE(TestControl); // 可以在TestControl.cpp 中通过d_func() 指向TestControlPrivate 的d_ptr 指针。
};
//TestControl.cpp
TestControl::TestControl():d_ptr(new TestControlPrivate)
{
d_ptr->q_ptr=this; //使用this指针初始化q_ptr.
}
void TestControl:TestFunc1(){
Q_D(TestControl); //成员函数中添加该宏,可以使用d代替d_func() 操作d_ptr.
....
d->TestFunc1(); //这里的d相当于d_ptr,这里调用TestControlPrivate 中的TestFunc1()成员函数。
...
}
//TestControlPrivate.h
class TestControlPrivate: public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(TestControl); //声明内联函数,可以通过q_func() 执行TestControl 的q_ptr 指针。
public:
TestControl *q_ptr; //定义q_ptr 指针。
void TestFunc1();
int TestFunc2();
};
//TestControlPrivate.cpp
void TestControlPrivate:TestFunc1()
{
qDebug<<"TestControlPrivate:TestFunc1() exe!\n";
}
void TestControlPrivate:TestFunc2()
{
Q_Q(TestControl); 成员函数中添加该宏,可以使用q代替q_func() 操作q_ptr.
q->TestFunc2(); //可以直接通过q指针调用TestControl 中的TestFunc2();
}