C++ 11多线程并发模式下实现带锁的std::cout,并发输

2023-09-13  本文已影响0人  FredricZhu

本例本意是模拟一个简单的barrier程序。就是使用线程组的join功能实现栅栏。但是在多线程输出的过程中,因为抢占控制台输出资源,会导致每个 operator<< 后面的输出被截断。相当于每次调用operator <<,是一次没有进行同步的写控制台操作,就会乱序。

然后谷歌了一下解决方案,可以继承一个std::stringstream,在继承类的析构函数中加锁,一次性全部输出rdbuf,避免中间被截断的问题。

方案比较简单。代码如下,
conanfile.txt

[requires]
boost/1.72.0

[generators]
cmake

CMakeLists.txt

cmake_minimum_required(VERSION 3.3)


project(3_thread_barrier)

set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")

set ( CMAKE_CXX_FLAGS "-pthread")
set(CMAKE_CXX_STANDARD 17)
add_definitions(-g)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

include_directories(${INCLUDE_DIRS})
LINK_DIRECTORIES(${LINK_DIRS})

file( GLOB main_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 

foreach( main_file ${main_file_list} )
    file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${main_file})
    string(REPLACE ".cpp" "" file ${filename})
    add_executable(${file}  ${main_file})
    target_link_libraries(${file} ${CONAN_LIBS} pthread)
endforeach( main_file ${main_file_list})

async_out.hpp

#ifndef _FREDRIC_ASYNC_OUT_HPP_
#define _FREDRIC_ASYNC_OUT_HPP_

#include <sstream>
#include <mutex>
#include <iostream>

struct AsyncOut: public std::stringstream {
    static inline std::mutex cout_mutex;

    ~AsyncOut() {
        std::lock_guard<std::mutex> lock(cout_mutex);
        std::cout << rdbuf();
        std::cout.flush();
    }
};

#define aout AsyncOut{}

#endif

main.cpp

#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <mutex>
#include "asyc_out.hpp"

int main(int argc, char* argv[]) {
    std::vector<std::thread> workers;
    for(int i=0; i<10; ++i) {
        workers.push_back(std::thread([i](){
            aout << "Hello from thread: " << i << "!\n";
        }));
    }

    // 每次运行 Hello from main的执行时机都不一样,充满了不确定性
    // Hello from thread 还有可能被截断
    // 我们希望每次的运行结果都一样
    aout << "Hello from main!\n";
    std::for_each(workers.begin(), workers.end(), [](std::thread& th) {
        th.join();
    });
  
    return EXIT_SUCCESS;
}

注意因为本例没有用到boost和其他三方库,所以可以直接用g++编译,注意链接上thread库就可以了。不一定非要CMake和conan加持。

程序输出如下,


image.png
上一篇 下一篇

猜你喜欢

热点阅读