即使经过漫长的等待,C ++线程也不会加入

时间:2017-08-18 11:04:35

标签: c++ boost-asio stdthread boost-test

我正在尝试使用boost单元测试来测试我的boost asio套接字侦听器。侦听器的目的只是侦听端口并读取所有内容并将其保存到队列中并发回http响应头。

作为第一步,我创建了一个客户端,用于向侦听器发送消息并读取来自侦听器的响应消息。我还创建了一个启动监听器的线程。主线程将发送和接收来自监听器的消息。我能够在客户端和列表器之间发送和接收消息。但是当我尝试加入它时,它不会加入并继续等待加入(我猜)。

请帮我解决问题。

提前致谢。

Listener.hpp

#ifndef Listener_hpp
#define Listener_hpp
#include <iostream>
#include <stdio.h>
#include <atomic>
#include <boost/asio.hpp>
#include "sharedQueue.hpp"

using namespace std;
using namespace boost;
using boost::asio::ip::tcp;

class Listener{

private:
int port;
std::atomic<bool> m_stop;
boost::system::error_code error;
boost::asio::io_service io_service;
std::shared_ptr<sharedQueue> queue_ptr;
void saveMessageToQueue(std::string,std::string);
Listener(int portToListen, std::shared_ptr<sharedQueue>  queue_unique_ptr);
Listener(const Listener&);
Listener& operator=(const Listener&);
public:
static Listener& getInstance(int portToListenOn,         std::shared_ptr<sharedQueue>   queue_unique_ptr);
void listen();
};
#endif /* Listener_hpp */

Listener.cpp

 #include "Listener.hpp"
 Listener::Listener(int portToListen, std::shared_ptr<sharedQueue>   queue_unique_ptr):port(portToListen), queue_ptr(queue_unique_ptr),m_stop(false)
 {;}
 Listener& Listener::getInstance(int portToListenOn, std::shared_ptr<sharedQueue>   queue_unique_ptr)
 {
  static Listener instance(portToListenOn,  queue_unique_ptr);
  return instance;
 }
 void Listener::stopListen(){
 m_stop = true;
 }
 void Listener::listen()
 {
  try{
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
    while(!m_stop)
    {
        std::cout<<"Server is listening on port "<<port<<std::endl;
        boost::asio::ip::tcp::socket socket(io_service);
        acceptor.accept(socket);
        std::cout<<"Connection Request Accepted "<<std::endl;
        std::array<char, 512> buf;
        socket.read_some(boost::asio::buffer(buf), error);
        std::string responseBack =" HTTP:300";
        boost::asio::write(socket, boost::asio::buffer(responseBack, responseBack.size()), error);
    }
  }
  catch (std::exception& e)
  {
    std::cerr <<"Listener Exception : "<< e.what() << std::endl;
    exit(0);
  }
  }

sharedQueue.hpp

  #ifndef SharedQueue_hpp
  #define SharedQueue_hpp
  #include <stdio.h>
  #include <iostream>
  class sharedQueue{
  public:
  sharedQueue(){};
  void pushToQueue(std::string str){};
  };
  #endif /* SharedQueue_hpp */

ClientTestHelper.hpp

  #ifndef ClientTestHelper_hpp
  #define ClientTestHelper_hpp

  #include <iostream>
  #include <boost/asio.hpp>
  using namespace std;
  class ClientTestHelper
  {
    string address = "";
    public:
    ClientTestHelper(std::string addressToPush){
    this->address = addressToPush;
   };
  ~ClientTestHelper(){};
  std::string sendMessage(string message);
  };

  #endif /* ClientTestHelper_hpp */

ClientTestHelper.cpp

#include "ClientTestHelper.hpp"
std::string ClientTestHelper::sendMessage(string message){
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(address, boost::asio::ip::resolver_query_base::numeric_service);
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
boost::asio::streambuf writeBuffer;
boost::asio::streambuf readBuffer;
std::string str = message;
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(str, str.size()), error);
std::array<char,1024> buf;
socket.read_some(boost::asio::buffer(buf), error);
std::string respo = buf.data();
cout<<respo<<std::endl;
return respo;
}

ListenerTest.hpp

#ifndef ListenerTest_hpp
#define ListenerTest_hpp

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN

#include <iostream>
#include <stdio.h>
#include <thread>
#include <boost/test/unit_test.hpp>
#include "Listener.hpp"
#include "ClientTestHelper.hpp"
#include "SharedQueue.hpp"

struct ListenerTestFixture{
public:
void startListen(Listener &receiver){
    receiver.listen();
}
std::shared_ptr<sharedQueue> myQueue  = std::make_shared<sharedQueue>();
Listener &receiver = Listener::getInstance(8282, myQueue);
void doRun(){
    receiver.listen();
}
ClientTestHelper *client = new ClientTestHelper("8282");
ListenerTestFixture(){};
~ListenerTestFixture(){};
};
#endif /* ListenerTest_hpp */

ListenerTest.cpp

#include "ListenerTest.hpp"

BOOST_FIXTURE_TEST_CASE(connectionTest, ListenerTestFixture)
{
std::thread t1(&ListenerTestFixture::doRun, *this);
std::string response = "";
response   = client->sendMessage("SomeRandom Messages \r\n\r\n\r\n");
//receiver.~Listener();
receiver.stopListen();
std::cout<<"Response Received "<<response;
t1.join();
std::cout<<"Finished Testing "<<endl;
}
  

控制台输出澄清:

Running 1 test case...
Server is listening on port 8585
Connection Request Accepted 
Server is listening on port 8585
  HTTP:300

此执行后没有停止而是等待加入。

  

编辑:修改后的Listener类添加了成员​​m_stop和stopListen(),将(;;)更改为while(!m_stop)。包含头文件。

2 个答案:

答案 0 :(得分:2)

receiver.~Listener();没有按照您的想法行事。 这行代码无法工作,必须出来。

它的作用是导致在receiver的所有成员上调用析构函数。它不会(直接)中断对listen()的调用。 可能的结果是执行listen()的线程将执行未定义的行为,并且几乎可以肯定这意味着无法以通知join()的方式结束。

简而言之,你的主线将永远等待,因为你已经从receiver()下面“拉出地毯”。

即使以某种方式工作,编译器也会在receiver.~Listener()的末尾添加一个main()的隐式调用,并且如果碰巧它可能会在那里崩溃。

正常的解决方案是将成员添加到Listener,例如std::atomic<bool> m_stop;(如果您使用Boost,则更喜欢Boost等效项)将其初始化为构造函数中的false(不是listen()),然后调用名为stop()的成员,在调用true之前立即将其设置为join()

然后使用listen()替换while(!stop)中的非终止循环。

这样你的主线程可以通知听取在循环结束时有序得出结论。

没有一般方法可以让线程“正好”或“突然”完全停止。您无法预测流程中的位置或使用的资源。 它可能在库调用中并破坏堆,操作系统,文件系统,任何东西。

另外,Java有一个名为stop()的线程方法,它在添加之后立即被弃用了。没有安全的方法来实现或使用它。

您很少直接调用析构函数。我知道的唯一用例是你使用了放置new并且不希望将空间返回堆。在现代C中它甚至更罕见,因为像容器中的回收空间这样的东西都被移交给std容器,如std::vector<>

std::vector<>的大多数实现都会调用析构函数,因为如果向下调整向量大小,则需要破坏对象但不重新分配内部存储。

你几乎不应该在静态分配的对象上调用析构函数。

答案 1 :(得分:0)

如果你正在运行asio函数,比如

acceptor.accept(socket);

除非他们已经完成或返回错误或异常(除了他们的底层套接字已设置为非阻塞模式!),否则它们将被阻塞。因此,如果已设置m_stop,则无法进行测试。

您需要取消stop()函数中的任何正在运行的操作,即:

void Listener::stopListen(){
    std::error_code _ignore;
    m_stop = true;
    m_listener.cancel(_ignore); // pass in an ec, it may throw otherwise
    m_sock.cancel(_ignore);
}

当然,你需要在你的类中嵌入io对象(socket / acceptor),以使它们在stopListen()中可以访问。

但是,说实话,我强烈建议用异步模式编写代码,并使用

m_acceptor.async_accept(m_sock, [&](const std::error_code &ec) {
    if (ec) {
        return handle_error(ec, "accept"); // bail out
    }

    handle_new_client(std::move(m_sock)); // move out socket
    // restart listen here
});

线程方法当然会引入额外的潜在竞争条件,有时难以正确处理。

Chris Kohlhoff有一些echo server examples具有不同的接受器样式,从阻塞到异步,显示工作模式。

干杯,   甲

S:read_some()很可能不会做你想要的。它只返回已经到达计算机TCP堆栈的前几个字节。但是,无法保证完整请求可用,因此您需要多次调用async_read。如果协议是http,asio :: async_read_until会更好,并使用&#34; \ r \ n&#34;分离器。