问题描述
从打印的 log 信息来看,所有线程均结束了搜索任务,但是并没有结束搜索任务。
问题分析
首先定位到搜索任务结束的代码位置,如下图所示;
从代码中可以看到,结束任务由activeTaskCount
变量和任务队列来控制,任务队列是 QTherd 库实现的接口函数,所以先对自定义的activeTaskCount
变量进行分析查看。
activeTaskcount
分析原代码可知,activeTaskCount
具体工作原理:
初始化与启动线程增加计数
在
onSearchButtonClicked
函数中,当开始搜索时,activeTaskCount
被初始化为0
。每当创建并启动一个
FileSearchThread
对象时,activeTaskCount
就加1
。代码如下:
1
2
3
4
5
6
7
8for (int i = 0; i < threadPool->maxThreadCount(); ++i) {
// 创建线程并连接信号槽
FileSearchThread *task = new FileSearchThread(searchKeyword, taskQueue, queueMutex, queueCondition);
connect(task, &FileSearchThread::fileFound, this, &FileSearch::onFileFound);
connect(task, &FileSearchThread::searchFinished, this, &FileSearch::onSearchFinished);
threadPool->start(task);
activeTaskCount++;
}线程完成任务时减少计数
当一个搜索线程完成一个搜索任务并触发
searchFinished
信号时,会调用onSearchFinished
槽函数。在
onSearchFinished
中,activeTaskCount
减1
,表示这个线程已经完成它的工作。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16void FileSearch::onSearchFinished() {
QMutexLocker locker(queueMutex);
activeTaskCount--;
progressBar->setValue(progressBar->value() + 1);
updateProgressLabel();
// 如果所有任务都完成了且任务队列为空,调用 finishSearch()
if (activeTaskCount == 0 && taskQueue->isEmpty()) {
finishSearch();
qint64 elapsedTime = timer.elapsed();
onSearchTime(elapsedTime);
progressBar->setValue(totalDirectories);
updateProgressLabel();
isSearching = false;
}
}
然而,activeTaskCount
的计数方式存在逻辑问题。当前代码在 FileSearchThread
完成一个任务并触发 searchFinished
时,直接减少 activeTaskCount
,然而这些线程会在任务队列中继续消费新的任务。这样就会导致 activeTaskCount
变得不准确,因为它没有考虑到同一个线程可能处理多个任务的情况。
从下面日志打印可以明显的看出来:
解决方法
确保每次线程从任务队列中取出新任务时,
activeTaskCount
正确增加。每当一个任务完成时,
activeTaskCount
减少,只有当所有任务都完成时才停止搜索。增加日志记录,帮助监控
activeTaskCount
的变化过程,以及线程在不同状态下的行为。
具体实现
- 添加
taskStarted
信号
首先,我们在 FileSearchThread
类中添加了一个 taskStarted
信号。每次线程从任务队列中取出一个新任务时,发出该信号,以便主线程更新 activeTaskCount
。
cpp
复制代码
1 | // FileSearchThread.h |
在 FileSearchThread::run()
方法中,当线程从队列中取出一个新任务时,发出 taskStarted
信号:
1 | void FileSearchThread::run() { |
- 更新
activeTaskCount
在 FileSearch
类中增加了一个 onTaskStarted
槽函数,用于在接收到 taskStarted
信号时增加 activeTaskCount
。
1 | void FileSearch::onTaskStarted() { |
同时,onSearchFinished
函数保持不变,每当线程完成任务时,减少 activeTaskCount
:
1 | void FileSearch::onSearchFinished() { |
- 增加日志记录
为了方便调试和监控,我们在以下位置添加了日志:
onTaskStarted()
和onSearchFinished()
中,记录activeTaskCount
的变化。FileSearchThread::run()
中,在线程等待新任务和退出时添加日志,帮助了解线程状态。onSearchButtonClicked()
中,记录搜索启动时的初始化状态。
新的问题
让我们回溯到最初的activeTaskCount
初始化逻辑:
1 | for (int i = 0; i < threadPool->maxThreadCount(); ++i) { |
每当创建并启动一个 FileSearchThread
对象时,activeTaskCount
就加 1
。
然而,实际上这个变量是件事处理中的任务的,并不是线程,所以只要把这里的 activeTaskCount++
删掉就好了。