多线程下智能指针

实例被其他线程依赖

当一个实例被多个线程引用,当线程并未结束时,此实例提前销毁会出现什么情况?
看下面例子
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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tadus_zeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值