簡體   English   中英

在多個線程中使用std :: cout

[英]using std::cout in multiple threads

我在c ++ 11中編寫了一個用於測試Thread的簡單程序,但是std::cout沒有像我期望的那樣工作。

class Printer
{
public:
    void exec()
    {
        mutex m;
        m.lock();
        cout<<"Hello  "<<this_thread::get_id()<<endl;
        chrono::milliseconds duration( 100 );
        this_thread::sleep_for( duration );
        m.unlock();

    }
};

int main()
{
    Printer printer;

    thread firstThread([&printer](){
        while(1)
            printer.exec();

    });
    thread secondThread([&printer](){
        while(1)
            printer.exec();
    });

    firstThread.join();
    secondThread.join();     
}

一些結果:

Hello 11376
Hello 16076
Hello 16076
Hello Hello 11376
16076
Hello 11376
,....

我使用互斥鎖來鎖定線程,所以我無法理解為什么兩個線程同時執行std::cout 它接縫非常適合我。任何人都可以解釋發生了什么!?!

線程使用不同的 mutex實例,因為mutexexec()函數中的局部變量,因此鎖定mutex是沒有意義的,因為每個線程將鎖定其自己的mutex導致線程之間沒有同步。 mutex必須使用相同的mutex實例才能實現同步。

要更正已發布的代碼,請將mutex成員變量。 但是,如果創建了另一個Printer對象,則使用不同Printer實例的線程之間將不會同步。 在這種情況下, mutex需要是一個static成員變量才能確保同步:

class Printer
{
public:
    //...
private:
    static std::mutex mtx_;
};

std::mutex Printer::mtx_;

要確保始終釋放mutex ,無論函數是正常退出還是通過異常退出,請使用std:lock_guard

std::lock_guard<std::mutex> lock(m); // 'm' locked, and will be
                                     // unlocked when 'lock' is destroyed.
std::cout<< "Hello  " << std::this_thread::get_id() << std::endl;
std::chrono::milliseconds duration( 100 );
std::this_thread::sleep_for( duration );

接受的答案是正確的。 然而,分開關注點很好:

  1. 您需要一種以線程安全的方式打印到std::cout方法。
  2. 您需要創建對象/仿函數/函數以在線程中運行並啟動它們。

這是我使用的一個實用程序,它專注於收集std::cout參數並在static std::mutex下將它們流式傳輸:

#include <iostream>
#include <mutex>

std::ostream&
print_one(std::ostream& os)
{
    return os;
}

template <class A0, class ...Args>
std::ostream&
print_one(std::ostream& os, const A0& a0, const Args& ...args)
{
    os << a0;
    return print_one(os, args...);
}

template <class ...Args>
std::ostream&
print(std::ostream& os, const Args& ...args)
{
    return print_one(os, args...);
}

std::mutex&
get_cout_mutex()
{
    static std::mutex m;
    return m;
}

template <class ...Args>
std::ostream&
print(const Args& ...args)
{
    std::lock_guard<std::mutex> _(get_cout_mutex());
    return print(std::cout, args...);
}

此代碼可以重用於除std::cout之外的流,但上述內容專門用於定位std::cout 有了這個,你的Printer::exec()現在可以大大簡化:

void exec()
{
    print("Hello ", std::this_thread::get_id(), '\n');
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

現在,您的Printer不僅會以線程安全的方式使用cout ,而且已經簡化(例如,不需要為cout維護自己的mutex ),但是所有其他類型和功能也可以使用cout並且所有安全地互操作。 print功能本身現在維護mutex ,這個事實被封裝在遠離所有print客戶端的位置。

我正在分享尼古拉斯在這個問題中給出的伎倆,我發現它比Howard Hinnant的實施更優雅。 我們的想法是創建一個臨時的ostringstream對象並將保護代碼放在析構函數上。

/** Thread safe cout class
  * Exemple of use:
  *    PrintThread{} << "Hello world!" << std::endl;
  */
class PrintThread: public std::ostringstream
{
public:
    PrintThread() = default;

    ~PrintThread()
    {
        std::lock_guard<std::mutex> guard(_mutexPrint);
        std::cout << this->str();
    }

private:
    static std::mutex _mutexPrint;
};

std::mutex PrintThread::_mutexPrint{};

然后,您可以從任何線程將其用作常規std::cout

PrintThread{} << "val = " << 33 << std::endl;

該對象將數據收集為常規std::ostringstream 一旦達到昏迷,就會銷毀對象並清除所有收集的信息。

您可以考慮全局std::mutex cout_mutex; (在命名空間中的某處),用於保護std::cout輸出。 確保使用std::lock<std::mutex> (這樣你就不會忘記解鎖互斥鎖和異常安全)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM