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

Posted

技术标签:

【中文标题】同时运行跟踪软件和服务器的正确方法是啥? (OpenCV 3)【英文标题】:What is the correct way to run tracking software and a server concurrently? (OpenCV 3)同时运行跟踪软件和服务器的正确方法是什么? (OpenCV 3) 【发布时间】:2016-08-13 11:17:29 【问题描述】:

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

谢谢!

编辑:我一直在尝试添加@Dom 建议的一些更改,但是我仍然遇到与以前类似的问题。我将在下面发布一些带有 cmets 的附加代码以尝试解释。

服务器::跟踪:

这是为了根据从客户端收到的命令启动跟踪。

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::run:

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::terminate:

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 组件似乎都已正确处理。如果我然后尝试再次打开跟踪,首先,跟踪似乎没有开始(但程序没有崩溃),然后如果我尝试停止跟踪,它会打印@ 987654345@,然后无限期停止而不打印flag 2

很抱歉这篇冗长的帖子。我希望这能为我想要实现的目标提供更多背景

【问题讨论】:

我完全不清楚,因为跳过了重要的方法定义(例如 Server::track),所以代码应该做什么......但是看起来,您没有与线程,你只需将它分离就可以了 - 似乎 player.setTrackStatus 什么都不做,它只是改变了玩家的成员值。首先,我会在一个线程(主线程)上做一些基础知识,然后将应用程序扩展为多线程,然后再尝试研究例如tutorialcup.com/cplusplus/multithreading.htm 对不起,我忘了提到那个功能。 Server::track 只需调用 player.run()。我不确定传递给新线程的函数是否可以在另一个类中。 setTrackStatus 函数更改 player 中的布尔值。此布尔值确定是否执行主跟踪循环(即while(tracking) ... )。谢谢,我会在没有线程的情况下尝试一下,然后尝试重新引入它 我试过不使用线程运行它,我遇到的问题已经解决了。但是,这现在意味着我的服务器在跟踪软件完成之前无法接受任何传入信号。我已阅读您推荐的教程。我知道您可以使用join(),但是,我不明白这将如何允许我同时运行进程,因为主线程必须等待它完成才能继续。感谢您的帮助! 太好了,你能更详细地描述你的愿望吗?因为看起来,并没有那么琐碎……应该同时做些什么呢?只是开始一首曲目然后停止曲目,还是同时播放更多曲目? (顺便说一句。我不确定,我们是在正确的地方进行长时间的讨论......)冷你编辑你的问题并添加“Server::track just calls player.run()。”? 一次只能运行一个跟踪软件实例。我需要运行服务器的循环与运行跟踪的循环同时运行(以便服务器仍然可以接收请求,例如停止跟踪的信号)。当然,我现在就编辑它 【参考方案1】:

所以你的跟踪应用程序。可以如下实现:

#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://***.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;

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

【讨论】:

感谢您抽出宝贵时间回答这个问题。这是我不会独立想出的东西。我会尝试你的建议并回复你 再次感谢您的帮助。不幸的是,我仍然无法解决这个问题。我试图采纳你的建议。我将更新我的帖子以包含更多我的代码。如果您能够提供帮助,那就太好了。我也会加cmets来尝试解释一下 欢迎 :-) 我给了你一个完整的例子(你可以通过按键 1 和 0 模拟启动/停止线程)。你了解每一行代码吗?如果不是或者您有任何疑问,请返回并逐行检查,研究 std::atomic、互斥体、线程的含义...... RAII 是什么,您将能够根据需要重新排列代码......这需要一些时间,但这是值得的 :-) 恐怕,我无法给你更好的建议,很抱歉。 谢谢。我想我只需要掌握您的示例中介绍的一些概念 我只是想我会很快更新你。我发现问题在于 imshow 和 namedWindow 函数以及多线程。我在开发时使用了imshow,但它只是我需要的数据。所以通过注释这些函数调用,我发现我可以在正确调用跟踪函数的情况下启动和重新启动我的程序

以上是关于同时运行跟踪软件和服务器的正确方法是啥? (OpenCV 3)的主要内容,如果未能解决你的问题,请参考以下文章

在 perl 中出现错误但没有堆栈跟踪的正确方法是啥?

在 Facebook Messenger 机器人中保存/跟踪状态的正确方法是啥?

使用关联实体的正确方法是啥?

Python - 在 asyncio 中运行函数的正确方法是啥?

让 gunicorn 运行的正确方法是啥?

在输入文本时保持居中的同时动态调整 UITextField 大小的正确方法是啥?