背景
我正在开发一个包含三个线程的Qt应用程序:main
,thread1
和thread2
。
main
创建,启动并显示thread1
和thread2
的结果
thread1
,同时也反复向他们提供输入。
thread2
每 n 执行一次密集计算(~1s)
输入,忽略所有其他输入。大部分时间都用于特征提取
使用Caffe framework。
thread1
对每个输入执行快速计算(~20ms)。但
每个 n + 1 输入取决于输入 n 到thread1
的输出。
在线程执行期间,thread2
在使用Caffe网络提取功能时似乎阻止了thread1
。但是,thread2
在其他处理步骤(例如网络输入预处理)中执行不阻止thread1
。
起初,我认为这是由未满足的依赖引起的:即thread2
“阻止”thread2
,因为输入(例如) 2n + 1 已准备就绪由thread1
处理,但输入 2n 尚未由thread2
完全处理。
然而,通过分析执行流程,我注意到在满足依赖性时发生了这种“阻塞”行为:即让 n = 10,thread1
将在输入15处暂停执行thread2
正在从输入20中提取Caffe功能。
问题
如何在Caffe功能提取过程中阻止thread1
阻止// !!! PROBLEM: ...
?
代码
下面是我的代码的精简版本,它显示了我的程序的关键组件和逻辑。
我在评论thread1worker.cpp
中突出显示了问题,可以在featureengine.cpp
和int main(int argc, char* argv[])
{
QApplication app(argc, argv);
qRegisterMetaType<Mat>("Mat");
Camera camera; // grabs camera frame every 30ms, emitting newFrame(frame)
/* thread1 */
QThread* thread1 = new QThread();
Thread1Worker* thread1_worker = new Thread1Worker();
thread1_worker->moveToThread(thread1);
QThread::connect(&camera, SIGNAL(newFrame(Mat)),
thread1_worker, SLOT(doWork(Mat)));
QThread::connect(thread1, SIGNAL(finished()),
thread1_worker, SLOT(deleteLater()));
QThread::connect(thread1, SIGNAL(finished()),
thread1, SLOT(deleteLater()));
/* thread2 */
ImageQueue* thread2_images = new ImageQueue();
QThread::connect(camera, SIGNAL(newFrame(Mat)),
thread2_images, SLOT(add(Mat)));
QThread* thread2 = new QThread();
Thread2Worker* thread2_worker = new Thread2Worker(thread2_images);
thread2_worker->moveToThread(thread2);
QThread::connect(thread2_worker, SIGNAL(workFinished(OutputType)),
thread2_worker, SLOT(addThread1Result(OutputType)));
QThread::connect(thread2, SIGNAL(finished()),
thread2_worker, SLOT(deleteLater()));
QThread::connect(thread2, SIGNAL(finished()),
thread2, SLOT(deleteLater()));
/* start threads */
thread1->start();
thread2->start();
camera->start();
return app.exec();
}
中找到。
main.cpp中:
Thread1Worker::Thread1Worker()
{
thread1_interval = 10; // this is "n"
is_initialized = false;
}
void Thread1Worker::doWork(Mat frame)
{
if (!is_initialized)
initialize();
// process only every nth frame
if (!isThread1Frame())
return;
// ... break frame up into multiple image patches
// !!! PROBLEM: this call blocks thread2
vector<vector<float> > features = feature_engine->extractFeatures(patches);
// ... use features to compute output
frame_count++;
emit workFinished(output);
}
void Thread1Worker::initialize()
{
InitGoogleLogging("caffe-demo");
feature_engine = new FeatureEngine();
is_initialized = true;
}
bool Thread1Worker::isThread1Frame()
{
return frame_count % thread1_interval == 0;
}
thread1worker.cpp:
void Thread2Worker::addThread1Result(OutputType output)
{
if (!is_initialized)
initialize();
thread1_output_queue.push(output);
thread1_count++;
processFrames();
}
void Thread2Worker::processFrames()
{
size_t num_process = (thread1_count * thread1_interval) - process_count;
size_t num_queue = thread2_images->size();
for (size_t i = 0; i < num_process && i < num_queue; i++)
{
Mat frame = thread2_images->get();
if (isThread1Frame()
{
curr_result = thread1_output_queue.front();
thread1_output_queue.pop();
}
else
{
curr_result = propagator->propagate(prev_result);
}
// update
prev_result = curr_result;
emit resultReady(curr_result);
}
}
void Thread2Worker::initialize()
{
propagator = new Propagator();
is_initialized = true;
}
bool Thread2Worker::isThread1Frame()
{
return process_count % thread1_interval == 0;
}
thread2worker.cpp:
vector<vector<float> > FeatureEngine::extractFeatures(const vector<Mat>& images)
{
// setup Caffe network for feature extraction:
Blob<float>* input_layer = net->input_blobs()[0];
int num_images = images.size();
int height = input_geometry.height;
int width = input_geometry.width;
input_layer->Reshape(num_images, num_channels, height, width);
net->Reshape();
vector<Mat> input_channels;
wrapInputLayer(input_channels);
preprocess(images, &input_channels);
// !!! PROBLEM: this ~1s computation blocks thread2
// details: https://github.com/BVLC/caffe/blob/master/src/caffe/net.cpp#L594
net->ForwardPrefilled();
// copy Caffe network output to features vector
vector<vector<float> > features;
// ...
return features;
}
featureengine.cpp:
{{1}}