我用c++写了一个基本的服务器,在无限while循环中运行。它从客户端接收信号来做一些事情。我想要的主要过程是启动或停止我编写的一些跟踪软件。
我希望服务器仍然能够接收信号,而跟踪软件正在运行(例如,如果一个停止信号被给出)。我认为最好的方法是为跟踪软件创建一个单独的线程,所以这就是我所做的:
void Server::tracking(Command c)
{
//I have since changed this method. The new implementation is below
//switch(c) {
// case START:
// player = VideoPlayer();
// player.setTrackStatus(true);
// t = std::thread(&Server::track, this);
// t.detach();
// break;
// case STOP:
// player.setTrackStatus(false);
// break;
// default:
// break;
//}
}
Server::track
只调用player.run()
VideoPlayer
是包含主跟踪环路的类。跟踪状态决定跟踪循环是否继续执行。
这工作很好,我第一次运行它,它能够开始跟踪和停止它。当我试图发送另一个"START"信号而不重新启动服务器时,问题就出现了。
我已经把问题缩小到cv::namedWindow
函数。以下是VideoPlayer
类的开始:
void VideoPlayer::run(void)
{
//I have since changed this method. The new implementation is below
//initVC();
//openStream();
}
initVC()
是我创建namedWindow
和openStream
包含主跟踪循环的地方。这里是initVC
(这是我认为问题所在):
void VideoPlayer::initVC()
{
if(!capture.open("cut.mp4")) {
throw "Cannot open video stream";
}
std::cout << "flag 1" << std::endl;
cv::namedWindow("Tracker", CV_WINDOW_AUTOSIZE);
std::cout << "flag 2" << std::endl;
}
我发现,在第二次运行(即跟踪已启动和停止,服务器尚未关闭和重新开放),flag 2
从未运行。我还发现,如果省略namedWindow
,则程序在imshow()
之前停止。值得注意的是,程序并没有崩溃,只是暂停了一下。
我有一种感觉,我在线程上做错了什么,因为我以前从未在c++中使用过线程。
谢谢!
EDIT:我一直在尝试添加@Dom建议的一些更改,但是我仍然有类似的问题。我将在下面发布一些附加代码,并附上注释来解释。
服务器:跟踪:
这意味着根据从客户端收到的命令启动跟踪。
void Server::tracking(Command c)
{
switch(c) {
case START:
if(!isRunning) {
player = make_unique<VideoPlayer>();
isRunning = true;
player->setTrackStatus(isRunning);
}
else {
std::lock_guard<std::mutex> lock(mtx);
}
break;
case STOP:
if(isRunning) {
player->terminate();
player->exit(); //Destroys OpenCV stuff
player->joinThread();
player = nullptr;
isRunning = false;
}
else {
std::lock_guard<std::mutex> lock(mtx);
}
break;
default:
break;
}
}
放像机构造函数:
VideoPlayer::VideoPlayer () : trackStatus(true)
{
tracker = Tracker(); //A separate class, related to the data from the tracked
//object. Not relevant to the current question
track_t = std::thread(&VideoPlayer::run, this);
return;
}
放像机:运行:
void VideoPlayer::run(void)
{
std::lock_guard<std::mutex> lock(mtx);
initVC(); //Initialises the OpenCV VideoCapture
openStream(); //Contains the main tracking code
return;
}
放像机:openStream:
void VideoPlayer::openStream()
{
while(trackStatus) {
... //tracking stuff
}
return;
}
放像机:终止:
void VideoPlayer::terminate()
{
track = false;
std::lock_guard<std::mutex> lock(mtx);
}
放像机:joinThread:
void VideoPlayer::joinThread()
{
if(track_t.joinable()) {
std::cout << "flag 1" << std::endl;
track_t.join();
std::cout << "flag 2" << std::endl; //It fails here on my second "run"
return;
}
}
基本上,我的程序在track_t.join()之前停止,这是我第二次运行跟踪(不重启服务器)。flag 1
和flag 2
打印我第一次运行跟踪。所有的OpenCV组件似乎都被正确地处理了。如果我然后尝试再次打开跟踪,首先,跟踪似乎没有开始(但程序不会崩溃),然后如果我试图停止跟踪,它打印flag 1
,但随后无限期停止而不打印flag 2
很抱歉写了这么长时间。我希望这能给我想要实现的目标提供更多的背景
所以你的跟踪应用程序可以实现如下:
#include <chrono>
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include <atomic>
enum Command : char
{
START = '1',
STOP = '0'
};
static std::mutex mtx; // mutex for I/O stream
class VideoPlayer
{
public:
VideoPlayer() : trackStatus()
{
initVC();
openStream();
};
~VideoPlayer()
{
closeStream();
uninitVC();
}
void setTrackStatus(bool status)
{
if (status && trackStatus == false)
{
trackStatus = status;
t = std::thread(&VideoPlayer::run, this);
}
else
{
trackStatus = false;
if (t.joinable())
{
t.join();
}
}
}
private:
void run()
{
tId = std::this_thread::get_id();
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "run thread: " << tId << std::endl;
}
while (trackStatus)
{
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "...running thread: " << tId << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(1)); // encode chunk of stream and play, whatever....
}
}
void initVC()
{
/*
if (!capture.open("cut.mp4"))
{
throw "Cannot open video stream"; --> http://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads
}
std::cout << "flag 1" << std::endl;
//cv::namedWindow("Tracker", CV_WINDOW_AUTOSIZE);
//std::cout << "flag 2" << std::endl;
*/
}
void uninitVC()
{
}
void openStream()
{
}
void closeStream()
{
}
private:
std::atomic<bool> trackStatus; // atomic, because of access from another (main) thread
std::thread t; // thread for "tracking"
std::thread::id tId; // ID of the "tracking" thread
};
class Server
{
public:
Server() : isRunning(), player(std::make_unique<VideoPlayer>())
{
}
~Server() = default;
void tracking(Command c)
{
switch (c)
{
case START:
if (!isRunning)
{
isRunning = true;
player->setTrackStatus(isRunning);
}
else
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Player is already running...n";
}
break;
case STOP:
if (isRunning)
{
player->setTrackStatus(!isRunning);
isRunning = false;
}
else
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Player is not running...n";
}
break;
default:
break;
}
}
private:
std::unique_ptr<VideoPlayer> player;
bool isRunning;
};
int main()
{
std::cout << "main thread: " << std::this_thread::get_id() << std::endl;
Server srv;
char cmd = -1;
while (std::cin >> cmd)
{
switch (cmd)
{
case Command::START:
{
srv.tracking(Command::START);
}
break;
case Command::STOP:
{
srv.tracking(Command::STOP);
}
break;
default:
std::cout << "Unknown command...n";
break;
}
}
}
你可以将创建的线程移动到VideoPlayer的构造函数中,并加入析构函数(我更喜欢它…):
VideoPlayer() : trackStatus(true)
{
initVC();
openStream();
t = std::thread(&VideoPlayer::run, this);
};
~VideoPlayer()
{
closeStream();
uninitVC();
if (t.joinable())
{
t.join();
}
}
但是需要一些修改来终止和清理线程,您可以使用像
这样的东西public:
void VideoPlayer::terminate()
{
{
std::lock_guard<std::mutex> lock(mtx);
std::cout << "terminate thread: " << tId << std::endl;
}
trackStatus = false;
}
但是,需要在START
期间创建player实例。player = std::make_unique<VideoPlayer>();
在STOP
期间终止()并删除播放器player->terminate();
player = nullptr;
希望,这对你有足够的启发;-)