什么是同时运行跟踪软件和服务器的正确方法?(OpenCV 3)



我用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()是我创建namedWindowopenStream包含主跟踪循环的地方。这里是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 1flag 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;

希望,这对你有足够的启发;-)

最新更新