boost :: asio io_service和std :: containers

时间:2016-04-20 07:41:57

标签: c++ multithreading boost-asio

我正在使用boost::asio构建网络服务,我不确定线程​​的安全性。

io_service.run()仅从专用于io_service工作的线程中调用一次

另一方面,

send_message()可以通过后面提到的第二个io_service处理程序中的代码调用,也可以通过用户交互时的mainThread调用。这就是我变得紧张的原因。

std::deque<message> out_queue;

// send_message will be called by two different threads
void send_message(MsgPtr msg){
    while (out_queue->size() >= 20){    
        Sleep(50);
    }
    io_service_.post([this, msg]() { deliver(msg);  });
}

// from my understanding, deliver will only be called by the thread which called io_service.run()
void deliver(const MsgPtr){
    bool write_in_progress = !out_queue.empty();
    out_queue.push_back(msg);
    if (!write_in_progress)
    {
        write();
    }
}

void write()
{
    auto self(shared_from_this());

    asio::async_write(socket_,
        asio::buffer(out_queue.front().header(),
        message::header_length),    [this, self](asio::error_code ec, std::size_t/)
    {

        if (!ec)
        {
            asio::async_write(socket_,
                asio::buffer(out_queue.front().data(),
                out_queue.front().paddedPayload_size()),
                [this, self](asio::error_code ec, std::size_t /*length*/)
            {
                if (!ec)
                {
                    out_queue.pop_front();
                    if (!out_queue.empty())
                    {
                        write();
                    }
                }

            });
        }

    });

}

这种情况安全吗?

类似的第二种情况:当网络线程收到消息时,它会将它们发布到另一个asio::io_service中,该std::unordered_map也由其自己的专用线程运行。这个io_service使用std::unordered_map<int, eventSink> eventSinkMap_; //... // called by the main thread (GUI), writes a callback function object to the map int IOReactor::registerEventSink(std::function<void(int, std::shared_ptr<message>)> fn, QObject* window, std::string endpointId){ util::ScopedLock lock(&sync_); eventSink es; es.id = generateRandomId(); // .... std::pair<int, eventSink> eventSinkPair(es.id, es); eventSinkMap_.insert(eventSinkPair); return es.id; } // called by the second thread, the network service thread when a message was received void IOReactor::onMessageReceived(std::shared_ptr<message> msg, ConPtr con) { reactor_io_service_.post([=](){ handleReceive(msg, con); }); } // should be called only by the one thread running the reactor_io_service.run() // read and write access to the map void IOReactor::handleReceive(std::shared_ptr<message> msg, ConPtr con){ util::ScopedLock lock(&sync_); auto es = eventSinkMap_.find(msg.requestId); if (es != eventSinkMap_.end()) { auto fn = es->second.handler; auto ctx = es->second.context; QMetaObject::invokeMethod(ctx, "runInMainThread", Qt::QueuedConnection, Q_ARG(std::function<void(int, std::shared_ptr<msg::IMessage>)>, fn), Q_ARG(int, CallBackResult::SUCCESS), Q_ARG(std::shared_ptr<msg::IMessage>, msg)); eventSinkMap_.erase(es); } 来存储回调函数等。

var sum=0;
for(var i=0;i<people.length; i++) {
    sum+= people[i].age;
}

首先:我甚至需要在这里使用锁吗?

Ofc两种方法都访问地图,但它们不访问相同的元素(receiveHandler无法尝试访问或读取尚未注册/插入地图的元素)。那线程安全吗?

1 个答案:

答案 0 :(得分:1)

首先,缺少很多上下文(onMessageReceived在哪里被调用,什么是ConPtr?你有太多问题。我会给你一些有用的指示你呢。

  1. 你应该在这里紧张:

    void send_message(MsgPtr msg){
        while (out_queue->size() >= 20){    
            Sleep(50);
        }
        io_service_.post([this, msg]() { deliver(msg);  });
    }
    

    除非out_queue是线程安全的,否则检查out_queue->size() >= 20需要同步。

    io_service_.post的调用是安全的,因为io_service是线程安全的。由于您有一个专用的IO线程,这意味着deliver()将在该线程上运行。现在,你也需要同步。

    我强烈建议在那里使用正确的线程安全队列。

  2.   

    问。首先:我是否需要在这里使用锁?

    是的,你需要锁定才能进行地图查找(否则你会在插入接收器的主线程中获得数据竞争)。

    需要在调用期间锁定(事实上,这似乎是一个非常不明智的想法,可能会导致性能问题或锁定)。由于Iterator invalidation rules,参考文件仍然有效。

    删除当然需要再次锁定。我会修改代码以立即删除和删除,并在释放锁之后调用接收器。 注意你必须在这里考虑异常(在你的代码中,当调用期间出现异常时,接收器不会被删除(永远?)。这对你来说很重要。

    <强> Live Demo

    void handleReceive(std::shared_ptr<message> msg, ConPtr con){
        util::ScopedLock lock(&sync_);
        auto es = eventSinkMap_.find(msg->requestId);
        if (es != eventSinkMap_.end())
        {
            auto fn  = es->second.handler;
            auto ctx = es->second.context;
            eventSinkMap_.erase(es); // invalidates es
    
            lock.unlock();
            // invoke in whatever way you require
            fn(static_cast<int>(CallBackResult::SUCCESS), std::static_pointer_cast<msg::IMessage>(msg));
        }
    }