实例被其他线程依赖
当一个实例被多个线程引用,当线程并未结束时,此实例提前销毁会出现什么情况?
看下面例子
main.cpp
#include <QtCore/QCoreApplication>
#include <Worker.h>
#include <DataItem.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
DataItem* dataItem = new DataItem();
Worker w1(dataItem);
w1.start();
Worker w2(dataItem);
w2.start();
delete dataItem;
return a.exec();
}
DataItem
#pragma once
#include <QObject>
class DataItem : public QObject
{
Q_OBJECT
public:
explicit DataItem();
virtual ~DataItem();
void SetData();
private:
int *m_iPtr;
};
#include "DataItem.h"
#include <QtCore>
#include <malloc.h>
DataItem::DataItem()
{
this->m_iPtr = (int*)_aligned_malloc(sizeof(int)*32,sizeof(int));
}
DataItem::~DataItem()
{
_aligned_free(this->m_iPtr);
}
void DataItem::SetData()
{
m_iPtr[qrand()%32] = qrand();
}
Worker
#pragma once
#include <QThread>
class DataItem;
class Worker : public QThread
{
Q_OBJECT
public:
explicit Worker(DataItem* dataItem);
virtual ~Worker();
void run() Q_DECL_OVERRIDE;
protected:
DataItem* m_dataItem;
};
#include "Worker.h"
#include "DataItem.h"
#include <QEventLoop>
Worker::Worker(DataItem* dataItem)
: m_dataItem(dataItem)
{
}
Worker::~Worker()
{
}
void Worker::run()
{
while (!this->isInterruptionRequested()) {
this->m_dataItem->SetData();
QThread::sleep(2);
QEventLoop().processEvents();
}
}
运行后得到一个经典错误, 访问冲突
如果把main函数改写成如下
#include <QtCore/QCoreApplication>
#include <Worker.h>
#include <DataItem.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
DataItem* dataItem = new DataItem();
Worker w1(dataItem);
w1.start();
Worker w2(dataItem);
w2.start();
w1.requestInterruption();
w2.requestInterruption();
w1.wait();
w2.wait();
delete dataItem;
return a.exec();
}
这样写自然是没问题,但在界面交互的情况下,dataItem的生命周期可能取决于用户的操作,而线程Worker 依赖于dataItem,
Worker的销毁时机也应该取决于dataItem。
利用智能指针改写如下
main.cpp
#include <QSharedPointer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
QSharedPointer<DataItem> dataItem(new DataItem());
Worker w1(dataItem);
w1.start();
Worker w2(dataItem);
w2.start();
QThread::sleep(2);
dataItem->toRelease();
w1.wait();
w2.wait();
} while (false);
return a.exec();
}
DataItem
#pragma once
#include <QObject>
class DataItem : public QObject
{
Q_OBJECT
public:
explicit DataItem();
virtual ~DataItem();
virtual void toRelease();
void SetData();
bool willRelease();
private:
int *m_iPtr;
bool m_bWillRelease;
};
#include "DataItem.h"
#include <QtCore>
#include <malloc.h>
DataItem::DataItem()
: m_bWillRelease(false)
{
this->m_iPtr = (int*)_aligned_malloc(sizeof(int) * 32, sizeof(int));
}
DataItem::~DataItem()
{
_aligned_free(this->m_iPtr);
}
void DataItem::toRelease()
{
this->m_bWillRelease = true;
}
void DataItem::SetData()
{
m_iPtr[qrand() % 32] = qrand();
}
bool DataItem::willRelease()
{
return m_bWillRelease;
}
worker
#pragma once
#include <QThread>
#include <QSharedPointer>
#include "DataItem.h"
class Worker : public QThread
{
Q_OBJECT
public:
explicit Worker(QSharedPointer<DataItem> dataItem);
virtual ~Worker();
void run() Q_DECL_OVERRIDE;
protected:
QSharedPointer<DataItem> m_dataItem;
};
#include "Worker.h"
#include "DataItem.h"
#include <QEventLoop>
#include <QDebug>
Worker::Worker(QSharedPointer<DataItem> dataItem)
: m_dataItem(dataItem)
{
}
Worker::~Worker()
{
qDebug() << qintptr(this) << " destory";
}
void Worker::run()
{
while (!this->isInterruptionRequested() && !m_dataItem->willRelease()) {
this->m_dataItem->SetData();
QThread::msleep(200);
QEventLoop().processEvents();
}
}
实例被回调函数(闭包/匿名函数/lambda表达式)依赖
当一个回调函数依赖一个外部实例时
如下例子
#include <QtCore/QCoreApplication>
#include "DataItem.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
DataItem item;
item.RequestData([&item]() {
item.ShowData();
});
} while (false);
return a.exec();
}
dataitem
#pragma once
#include <QObject>
#include <functional>
class DataItem : public QObject
{
Q_OBJECT
public:
DataItem(QObject *parent = Q_NULLPTR);
~DataItem();
virtual void RequestData(std::function<void()> callback);
virtual void ShowData() const;
quint8 m_data[1024];
};
#include "DataItem.h"
#include <QDebug>
#include <QThread>
class MyRequest : public QThread {
public:
explicit MyRequest(std::function<void()> callback,QObject* parent=Q_NULLPTR)
: QThread(parent)
, m_callback(callback)
{}
void run() Q_DECL_OVERRIDE {
QThread::sleep(3);
this->m_callback();
this->deleteLater();
}
std::function<void()> m_callback;
};
DataItem::DataItem(QObject *parent)
: QObject(parent)
{
}
DataItem::~DataItem()
{
}
void DataItem::RequestData(std::function<void()> callback)
{
(new MyRequest(callback))->start();
}
void DataItem::ShowData() const
{
for (const auto& data : m_data) {
qDebug() << data;
}
}
运行上述例子又得到一个经典错误,访问冲突
我们知道回调函数本质上是一个类,回调函数引用外部的变量实质变成了回调函数对象的成员变量,使用共享指针可以很快的解决问题
#include <QtCore/QCoreApplication>
#include <QSharedPointer>
#include "DataItem.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
auto item = QSharedPointer<DataItem>::create();
item->RequestData([item]() {
item->ShowData();
});
} while (false);
return a.exec();
}
但我是想让回调函数依赖DataItem,DataItem被销毁,回调函数不应该再继续,可以使用弱引用指针。
#include <QtCore/QCoreApplication>
#include <QPointer>
#include <QDebug>
#include <QThread>
#include "DataItem.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
QPointer<DataItem> item(new DataItem());
item->RequestData([item]() {
if (item.isNull()) {
qDebug()<<"DataItem is Released";
return;
}
item->ShowData();
});
QThread::sleep(1);
item->deleteLater();
} while (false);
return a.exec();
}
this 被其他actor(对象或线程)依赖时
将上面的例子改写成如下
#include <QtCore/QCoreApplication>
#include <QSharedPointer>
#include <QDebug>
#include <QThread>
#include "DataItem.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
DataItem item;
item.RequestData();
} while (false);
return a.exec();
}
dataitem
#pragma once
#include <QObject>
#include <QSharedPointer>
#include <functional>
#include <memory>
class DataItem : public QObject
{
Q_OBJECT
public:
DataItem(QObject *parent = Q_NULLPTR);
~DataItem();
virtual void RequestData();
virtual void ShowData() const;
quint8 m_data[1024];
};
#include "DataItem.h"
#include <QDebug>
#include <QThread>
class MyRequest : public QThread {
public:
explicit MyRequest(std::function<void()> callback, QObject* parent = Q_NULLPTR)
: QThread(parent)
, m_callback(callback)
{}
void run() Q_DECL_OVERRIDE {
QThread::sleep(2);
this->m_callback();
this->deleteLater();
}
std::function<void()> m_callback;
};
DataItem::DataItem(QObject *parent)
: QObject(parent)
{
}
DataItem::~DataItem()
{
}
void DataItem::RequestData()
{
auto self = this;
(new MyRequest([self]() {
self->ShowData();
}))->start();
}
void DataItem::ShowData() const
{
for (const auto& data : m_data) {
qDebug() << data;
}
}
运行上面会得到访问冲突
需要将this指针被其他线程依赖,可是C/C++的原始指针是没有空检测能力的
为了让实例自身有能力表达出自身已经被销毁了的这个状态,可以如下
#pragma once
#include <QObject>
#include <QSharedPointer>
#include <functional>
#include <memory>
class DataItem : public QObject
{
Q_OBJECT
public:
DataItem(QObject *parent = Q_NULLPTR);
virtual ~DataItem();
virtual void RequestData();
virtual void ShowData() const;
virtual QWeakPointer<DataItem> WeakSelf() const;
quint8 m_data[1024];
private:
QSharedPointer<DataItem> m_self;
};
#include "DataItem.h"
#include <QDebug>
#include <QThread>
class MyRequest : public QThread {
public:
explicit MyRequest(std::function<void()> callback, QObject* parent = Q_NULLPTR)
: QThread(parent)
, m_callback(callback)
{}
void run() Q_DECL_OVERRIDE {
QThread::sleep(2);
this->m_callback();
this->deleteLater();
}
std::function<void()> m_callback;
};
DataItem::DataItem(QObject *parent)
: QObject(parent)
, m_self(this, [](DataItem*) {})
{
}
DataItem::~DataItem()
{
m_self.clear();
}
void DataItem::RequestData()
{
auto self = this->WeakSelf();
(new MyRequest([self]() {
if (self.isNull()) {
qDebug() << "DataItem is released";
return;
}
self.data()->ShowData();
}))->start();
}
void DataItem::ShowData() const
{
auto self = this->WeakSelf();
for (const auto& data : m_data) {
if (self.isNull()) return;
qDebug() << data;
}
}
QWeakPointer<DataItem> DataItem::WeakSelf() const
{
return m_self.toWeakRef();
}
但是这样写还是会有很高的概率DataItem实例在self.data()->ShowData();
这句及之后被销毁,造成野指针,弹出内存访问冲突。
我们可以引入SharedFromThis
在C++里面是std::enable_shared_from_this
, 在Qt里面是 QEnableSharedFromThis
main
#include <QtCore/QCoreApplication>
#include <QSharedPointer>
#include <QDebug>
#include <QThread>
#include "DataItem.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
do {
QSharedPointer<DataItem> item = QSharedPointer<DataItem>::create();
item->RequestData();
QThread::msleep(20);
item->RequestData();
} while (false);
return a.exec();
}
dataitem
#ifndef DATAITEM_H
#define DATAITEM_H
#include <QObject>
#include <QSharedPointer>
#include <QEnableSharedFromThis>
class DataItem : public QObject,public QEnableSharedFromThis<DataItem>
{
Q_OBJECT
public:
explicit DataItem();
virtual ~DataItem();
virtual void RequestData();
virtual void ShowData() const;
virtual void ToRelease() { m_bWillRelease = true; }
virtual bool willRelease() const { return m_bWillRelease;}
quint8 m_data[1024];
protected:
bool m_bWillRelease;
};
#endif // DATAITEM_H
#include "DataItem.h"
#include <QDebug>
#include <QThread>
#include <functional>
class MyRequest : public QThread {
public:
explicit MyRequest(std::function<void()> callback, QObject* parent = Q_NULLPTR)
: QThread(parent)
, m_callback(callback)
{}
void run() Q_DECL_OVERRIDE {
QThread::sleep(2);
this->m_callback();
this->deleteLater();
}
std::function<void()> m_callback;
};
DataItem::DataItem() :QObject(Q_NULLPTR),m_bWillRelease(false) {}
DataItem::~DataItem() {
if(!willRelease())ToRelease();
qDebug() << "DataItem release";
}
void DataItem::RequestData()
{
auto self = sharedFromThis();
(new MyRequest([self]() {
if (self->willRelease()) { return; }
self->ShowData();
}))->start();
}
void DataItem::ShowData() const
{
for (const auto& data : m_data) {
if (willRelease()) { return; }
qDebug() << data;
}
}