线程上的Boost.Asio并发问题

时间:2015-05-17 21:06:40

标签: c++ multithreading c++11 boost

我正在尝试制作具有一些功能的程序:

- 使用Boost.Asio进行网络连接(是的,我得到了大部分内容)。

- 使用Boost.Thread和Boost.Asio进行多线程处理。

该程序的重​​要方面是一些线程,一个将不断尝试读取,一个将检查std :: vector的内容并将它们写入文本框(我使用Nana C ++进行GUI) )和插座上。 所以我知道我需要在某些方面使用原子,因为我还需要这些线程能够关闭自己(使用bool可能?)并且能够在向量中读取和写入。但这证明非常困难。

我真正想知道的是: 如何让这些线程执行写入和读取操作以及访问向量而不会损坏数据或导致未定义的行为? 如何在不造成任何伤害的情况下“杀死”线程?

我知道这很混乱,所以以下是我到目前为止构建的代码:

short port;
wstring password;
atomic<bool> stop = false;
vector<wstring> msgQueue;
atomic<vector<wstring>> sLog;
boost::asio::io_service io_service;

class session
{
public:
    session(boost::asio::io_service& io_service)
        : socket_(io_service)
    {
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        thread(read);
        while (!stop)
        {
            if (!msgQueue.empty())
            {
                std::string sending = ws2s(msgQueue.front());
                msgQueue.erase(msgQueue.begin());
                socket_.write_some(boost::asio::buffer(sending.c_str(), sending.length()));
            }
        }
    }

    void read()
    {
        while (!stop)
        {
            socket_.read_some(boost::asio::buffer(data_, max_length));
        }
    }

    void handle_read(const boost::system::error_code& error,
        size_t bytes_transferred)
    {
        if (!error)
        {
            //boost::asio::async_write(socket_,
                //boost::asio::buffer(data_, bytes_transferred),
                //);
        }
        else
        {
            delete this;
        }
    }

    void handle_write(const boost::system::error_code& error)
    {
        if (!error)
        {
            socket_.async_read_some(boost::asio::buffer(data_, max_length),
                boost::bind(&session::handle_read, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }
        else
        {
            delete this;
        }
    }

private:
    tcp::socket socket_;
    enum { max_length = 1024 };
    char data_[max_length];
};

class server
{
public:
    server(boost::asio::io_service& io_service, short port)
        : io_service_(io_service),
        acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
    {
        session* new_session = new session(io_service_);
        acceptor_.async_accept(new_session->socket(),
            boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }

    void handle_accept(session* new_session,
        const boost::system::error_code& error)
    {
        if (!error)
        {
            new_session->start();
            new_session = new session(io_service_);
            acceptor_.async_accept(new_session->socket(),
                boost::bind(&server::handle_accept, this, new_session,
                boost::asio::placeholders::error));
        }
        else
        {
            delete new_session;
        }
    }

private:
    boost::asio::io_service& io_service_;
    tcp::acceptor acceptor_;
};

    void praca()
    {
        try
        {
            server s(io_service, port);
            sLog; // push_back() here?
            io_service.run();
        }
        catch (std::exception& e)
        {
            std::string raps(const_cast<char*>(e.what()));
            MsgBox(L"Error", s2ws(raps));
        }
    }

    void start()
    {
        stop = false;
        thread(praca);
        append(L"Server is now running. Waiting for client connection...", false);
    }

我知道这段代码存在重大缺陷,如果编译它肯定会导致很多异常。

我也忘了提及,我想在发送/接收时使用密码,或者只是在连接时使用密码,但我想我可以在目前的混乱解决后解决这个问题。

关于我如何做到这一点的任何建议,或者我应该使用其他东西而不是我到目前为止所尝试的东西(方法或库)?

顺便说一下,我在Windows 8.1上使用Visual Studio 2013。

提前致谢。

1 个答案:

答案 0 :(得分:1)

如果您为数据使用std::vector,则需要使用boost::mutex(或类似)来确保读写线程不会同时访问数据时间。我建议阅读boost documentation on synchronization我保证会回答你关于如何确保你没有两个线程一次访问数据的问题。

另一种选择是当你找到你想要写的数据时,立即告诉你的Writer类/线程。我提供了一个这样的例子

class Writer
{
public:
    Writer() : io_service(), writerThread(&Writer::WriterThread, this), threadRunning(true) {}

    ~Writer()
    {
        // tell the thread to stop
        threadRunning = false;
        // wait for the thread to stop
        writerThread.join();
    }

    void AddDataToWrite(const std::string& sData) { io_service.post(boost::bind(&std::vector<std::string>::push_back, boost::ref(dataToWrite), sData)); }

private:
    void WriterThread()
    {
        // while the thread is running, process logic:
        while (threadRunning)
        {
            // Check for new work:
            io_service.run();
            // Prepare for new work:
            io_service.reset();

            // Process any work:
            std::vector<std::string>::iterator it = dataToWrite.begin();
            while (it != dataToWrite.end())
            {
                // Write the data:
                std::cout << *it << std::endl;
                // Remove the data:
                it = dataToWrite.erase(it);
            }
        }
    }

    boost::asio::io_service io_service;
    boost::thread writerThread;
    bool threadRunning; // NB: make this atomic!
    std::vector<std::string> dataToWrite;

};

int main()
{
    Writer w;
    for ( int i = 0;; ++i)
    {
        if (i % 10000 == 0)
        {
            w.AddDataToWrite("Hello");
        }
    }
    return 0;
}

Writer类将创建一个线程并处理其中的所有工作。调用Writer::AddDataToWrite时,boost::asio::io_service会将工作发布到线程(WriterThread)进行处理。这里的示例显示了两个线程:主线程(main()的内容)和writer线程(Writer::WriterThread的内容)。

此示例还回答了关于如何确保线程关闭而不会造成伤害的第二个问题。使用bool:创建线程时,threadRunning设置为true。线程使用此bool并在运行之前检查其true。在Writer的析构函数中,bool设置为false。这意味着线程将停止处理数据,函数将终止并且线程将完成。对join()的调用等待发生(因此如果线程需要运行几秒钟,您的应用程序将等到它完成)。这很重要,因为您的线程可能正在使用在调用析构函数(或其他析构函数)后将被释放的资源。

请务必注意threadRunning应为atomic。这是因为bool在两个线程中使用:bool在编写器线程上相当频繁地读取,但Writer析构函数将在主线程上调用(其中bool析构函数1}}将设置为false)。

相关问题