【问题标题】:std::stringstream across threads doesn't work as expected跨线程的 std::stringstream 无法按预期工作
【发布时间】:2020-09-08 08:40:34
【问题描述】:

我正在使用std::iostreams 在线程之间发送流数据,但难以提取数据,这是一个人为的示例:

#include <sstream>
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <mutex>

using namespace std::chrono_literals;

int main()
{
    auto stream = std::stringstream{};
    auto stop = std::atomic_bool{false};
    auto mtx = std::mutex{};

    auto thread = std::thread{[&]() {
        while(!stop) {
            auto lock = std::lock_guard{mtx};
            if (stream.peek() != decltype(stream)::traits_type::eof()) {
                auto str = std::string{};
                stream >> str;
                std::cout << str;
            }
        }
    }};

    {
        // Make sure thread is running before sending data
        std::this_thread::sleep_for(100ms);
        {
            auto lock = std::lock_guard{mtx};
            stream << "hello" << std::endl;
        }

        // Give the thread a chance to receive it
        std::this_thread::sleep_for(100ms);
    }

    stop = true;
    thread.join();

    return EXIT_SUCCESS;
}

Running it,没有输出 - 调试显示stream.peek() 总是 EOF。我一定是做错了什么,但我看不到!

【问题讨论】:

    标签: c++ multithreading iostream


    【解决方案1】:

    问题是由于您在读取数据之前读取数据(它被写入)。

    当您尝试从空流中读取数据时,错误标志被设置,然后在清除错误标志之前不会执行此类流上的任何后续读/写操作。

    你需要一些能确保在写完之后再读的东西。 std::condition_variable 可以做到。

    https://godbolt.org/z/fs9TGW

    int main()
    {
        auto stream = std::stringstream{};
        auto mtx = std::mutex{};
        auto hasData = std::condition_variable{};
        auto readingStarted = std::condition_variable{};
    
        auto thread = std::thread{[&]() {
            std::unique_lock<std::mutex> lock{mtx};
            readingStarted.notify_one();
            hasData.wait(lock);
            auto str = std::string{};
            stream >> str;
            std::cout << str;
        }};
    
        {
            auto lock = std::unique_lock{mtx};
            readingStarted.wait(lock);
            stream << "hello" << std::endl;
            hasData.notify_one();
        }
    
        thread.join();
    
        return EXIT_SUCCESS;
    }
    

    【讨论】:

    • 我没有在实际代码中使用睡眠...但是您对错误标志是正确的,我没有意识到它们需要手动清除。
    • 更多的是关于同步而不是清除标志。
    • @cmannett85,请不要将此答案中的示例用作生产中的模板。由于spurious wakes up,使用不带共享变量(条件)的conditional variable 是一种容易出错的方式。 This article 给出了很好的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 2014-12-09
    • 2016-01-13
    • 2020-09-21
    相关资源
    最近更新 更多