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

标签 c++ multithreading opencv

我用 C++ 编写了一个在无限循环中运行的基本服务器。它接收来自客户端的信号来做事。我想要的主要过程是启动或停止我编写的一些跟踪软件。

我希望服务器在跟踪软件运行时仍然能够接收信号(例如,如果发出停止信号)。我认为最好的方法是为跟踪软件创建一个单独的线程,所以这就是我所做的:

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++ 中使用过线程。

谢谢!

编辑: 我一直在尝试添加@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::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;
}

VideoPlayer::运行:

void VideoPlayer::run(void)
{
  std::lock_guard<std::mutex> lock(mtx);
  initVC(); //Initialises the OpenCV VideoCapture
  openStream(); //Contains the main tracking code
  return;
}

VideoPlayer::openStream:

void VideoPlayer::openStream() 
{
  while(trackStatus) {
  ... //tracking stuff
  }
  return;
}

VideoPlayer::终止:

void VideoPlayer::terminate()
{
  track = false;
  std::lock_guard<std::mutex> lock(mtx);
}

VideoPlayer::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 = std::make_unique<VideoPlayer>();

然后 Terminate() 并在 STOP 期间删除播放器

player->terminate();
player = nullptr;

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

关于c++ - 同时运行跟踪软件和服务器的正确方法是什么? (OpenCV 3),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38932120/

相关文章:

c++ - 此代码的最坏情况?

python - 如何使用Tensorflow在视频中的对象检测中获取预测边界框的坐标(甚至中心点)

Python视频流到C++服务器

opencv - OpenCV中的坐标转换

c++ - 存储多种颜色并从一种颜色切换到另一种颜色

c++ - C++ 大数快速排序堆栈溢出

c# - 为什么我的 .Net 应用程序只使用单个 NUMA 节点?

c# - 多核机器上的多线程没有最大化 CPU

c++ - Eclipse C++11 线程支持不起作用

c++ - 将带有字段的表作为参数从 C++ 传递给 Lua 函数?