Qt 多线程复制文件 QFile::copy() 一个奇怪的问题

王桑的一天 2020-10-15 03:02:08
我想用生产消费模式,多线程复制文件,使用静态方法 QFile::copy()
执行一半就会崩溃,只复制了一部分。
如果我在 QFile::copy() 下面加一句 qDebug() << "hello"; ,也就是consumer.cpp 第24行,就不会崩溃

QFile::copy(src, dst);
qDebug() << "hello";


这是什么原因呢?

producer.h



struct MoveInfo {
bool rm;
QString src;
QString dst;
};

enum TAG {
COUNT_FILE = 0,
HANDLE = 1,
};

static QSemaphore freeSpace(8); // 控制任务队列缓冲长度
static QSemaphore usedSpace(0);
static QQueue<MoveInfo> task_q;


class Producer : public QThread
{
Q_OBJECT

public:
Producer(QObject *parent=nullptr);
~Producer();

void run();
void setParams(QString, QString, bool);

signals:
void mainMsgChanged(QString, QColor c=Qt::black);
void subMsgChanged(QString);
void completed();

private:
QString root_path;
int total = 0;
int count = 0;
QString src_path;
QString dst_path;
bool rm;
void walkPath(QString, QString, TAG);
};



producer.cpp


Producer::Producer(QObject *parent)
:QThread (parent)
{

}

void Producer::run()
{
qDebug().noquote() << "Source path:" << src_path;
total = 0;
count = 0;
emit mainMsgChanged(tr("正在统计数量..."));
walkPath(src_path, dst_path, TAG::COUNT_FILE);
qDebug().noquote() << "Total number of files:" << total;
count = 0;
emit mainMsgChanged(tr("正在处理..."));
walkPath(src_path, dst_path, TAG::HANDLE);
emit mainMsgChanged(tr("Ready"));

if(total != 0)
emit subMsgChanged(QString("[%1%] %2/%3").arg(int(100*((float)count/total))).arg(count).arg(total));
else
emit subMsgChanged(QString("[%1%] %2/%3").arg(100).arg(count).arg(total));

// 没有更多文件了,放入空信息通知消费线程退出
MoveInfo info;
for (int i = 0; i < 8; i++)
{
freeSpace.acquire();
task_q.enqueue(info);
usedSpace.release();
}
}

void Producer::walkPath(QString src, QString dst, TAG t)
{
QDir src_dir(src);
QDir dst_dir(dst);
QFileInfoList src_files = src_dir.entryInfoList(QStringList{"*.xls"}, QDir::Files | QDir::NoDotAndDotDot);
MoveInfo info;

if (t == TAG::COUNT_FILE)
emit subMsgChanged(QDir::toNativeSeparators(src)); // 通知界面当前目录

for(auto file : src_files)
{
if(t == TAG::COUNT_FILE) // 先统计文件总数
{
total++;
} else {
info.rm = rm;
info.src = file.filePath();
info.dst = dst_dir.filePath(file.fileName());
freeSpace.acquire();
task_q.enqueue(info); // 将文件信息放入队列
usedSpace.release();
count++;
//通知界面更新进度
emit subMsgChanged(QString("[%1%] %2/%3").arg(int(100*((float)count/total))).arg(count).arg(total));
}
}
QStringList src_dirs = src_dir.entryList(QStringList{}, QDir::Dirs | QDir::NoDotAndDotDot);

// 递归处理所有子目录
for(auto sd : src_dirs)
{
if(!dst_dir.exists(sd) && t == TAG::HANDLE)
dst_dir.mkdir(sd);
walkPath(src_dir.filePath(sd), dst_dir.filePath(sd), t);
}
}

Producer::~Producer()
{

}



consumer.h 消费者


class Consumer : public QThread
{
Q_OBJECT
public:
Consumer(QObject *parent=nullptr);
~Consumer();
void run();
};


consumer.cpp 消费者


Consumer::Consumer(QObject *parent)
: QThread(parent)
{

}

void Consumer::run()
{
MoveInfo info;
qDebug() << currentThreadId();

while (true)
{
usedSpace.acquire();
info = task_q.dequeue(); // 获取一个文件信息
freeSpace.release();

if (info.src.isEmpty()) // 如果文件名为空,则中断,结束
break;

if(!QFileInfo::exists(info.dst))
{
QFile::copy(info.src, info.dst);
qDebug() << "hellow world"; // 不加这一句就会崩溃
}

if(info.rm)
QFile::remove(info.src);
}
}

Consumer::~Consumer()
{

}



void MainWindow::start()
{
mProducer.setParams("D:/files", "D:/target/", false);
mProducer->start();
for (int i = 0; i < 8; i++)
{
Consumer *c = new Consumer();
connect(c, &Consumer::finished, c, &Consumer::deleteLater);
c->start();
}
}
...全文
16721 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
mideum 2020-10-16
  • 打赏
  • 举报
回复
引用 6 楼 管理員 的回复:
qDebug() 内部加的锁会影响我的代码吗?

再想想好像没啥大影响
芒果黑 2020-10-16
  • 打赏
  • 举报
回复
引用 2 楼 管理員 的回复:
但是为什么下面加一句 qDebug() 输出,就不会出问题呢?
可能增加了一个debug的时间就错开了对task_q的同时操作
王桑的一天 2020-10-16
  • 打赏
  • 举报
回复
但是为什么下面加一句 qDebug() 输出,就不会出问题呢?
王桑的一天 2020-10-16
  • 打赏
  • 举报
回复
引用 5 楼 mideum 的回复:
因为qDebug内部代码有加锁呀
qDebug() 内部加的锁会影响我的代码吗?
mideum 2020-10-16
  • 打赏
  • 举报
回复
因为qDebug内部代码有加锁呀
王桑的一天 2020-10-16
  • 打赏
  • 举报
回复
引用 3 楼 芒果黑 的回复:
[quote=引用 2 楼 管理員 的回复:]但是为什么下面加一句 qDebug() 输出,就不会出问题呢?
可能增加了一个debug的时间就错开了对task_q的同时操作[/quote] 感觉说不通。。。不管加什么,理论上都还是会有冲突的可能吧
王桑的一天 2020-10-15
  • 打赏
  • 举报
回复
找到原因了,就是 task_q.enqueue() / dequeue() 的时候需要加锁……

16,212

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧