具有线程安全std :: cout的死锁

史蒂夫·默多克

我正在尝试使用EnterCriticalSectionLeaveCriticalSection的Windows API来实现std :: cout的简单,线程安全的变通方法我认为示例代码是不言自明的,因此我们开始:

#include <cstdlib>
#include <iostream>
#include <windows.h>

namespace mynamespace {

class MyStream {
    public:
        MyStream(void) : m_lockOwner(0) {
            ::InitializeCriticalSection(&m_lock);
        }

        ~MyStream(void) {
            ::DeleteCriticalSection(&m_lock);
        }


        template <typename T>
        MyStream& operator<<(const T& x) {
            Lock();
            std::cout << x;

            return *this;
        }

        void Lock() {
            ::EnterCriticalSection(&m_lock);  // Try to get the lock, irrelevant which thread it is
            // One thread successfully received the lock and entered the critical section
            if(m_lockOwner == ::GetCurrentThreadId()) {
                // Decrease the lock count of a thread when it entered multiple times the critical section
                // e.g. mynamespace::stream << "critsec1" << "critsec2"; mynamespace::stream << mynamespace::endl;
                ::LeaveCriticalSection(&m_lock);
            }
            // Store the thread ID of the thread that holds the lock at the moment
            m_lockOwner = ::GetCurrentThreadId();
        }

        void Unlock() {
           if(m_lockOwner == GetCurrentThreadId()) {
               // Release the lock only if the calling thread is the owner
               // Note: This should be the last decrease of the lock count

               // Also reset the ownership of the lock,
               // e.g. for the case that one thread is able to enter the critical section two times in a row
               // mynamespace::stream << "crit first" << mynamespace::endl;
               // mynamespace::stream << "crit second" << mynamespace::endl;
               m_lockOwner = 0;
               ::LeaveCriticalSection(&m_lock);
           }
        }

        MyStream& operator<<(MyStream& (*endl)(MyStream&)) {
            return endl(*this);
        }

        DWORD            m_lockOwner;
        CRITICAL_SECTION m_lock;
    };

    static MyStream& endl(MyStream& stream) {
        std::cout << std::endl;
        stream.Unlock();
        return stream;
    }

    MyStream stream;

};

bool alive = true;

DWORD my_thread(LPVOID t) {
    while(alive) {
        mynamespace::stream << "OWN THREAD" << mynamespace::endl;
    }

    return 0;
}

void waitForThread(HANDLE th) {
    alive = false;

    // Wait for thread to finish
    (void)::WaitForSingleObject(th, INFINITE);

    ::CloseHandle(th);

    th = 0;
}

int main(void) {
    HANDLE th = ::CreateThread(
        NULL,
        0,
        reinterpret_cast<LPTHREAD_START_ROUTINE>(&my_thread),
        NULL,
        0,
        NULL);

    mynamespace::stream << "test print 1" << "test print 2";
    mynamespace::stream << mynamespace::endl;
    mynamespace::stream << "test print 3";
    ::Sleep(10);
    mynamespace::stream << mynamespace::endl;
    ::Sleep(10);

    waitForThread(th);

    return EXIT_SUCCESS;
}

在我的第一篇文章中,我提到退出程序有时会遇到死锁。

这里的问题是,LeaveCriticalSection当我通过一个线程进入关键部分时,我没有那么频繁地打电话另一个问题是我没有正确退出子线程。

有了egurs的帮助和我的一些小补充,此流现在应该是线程安全的。

招呼

木头

多次输入关键部分,从而增加了其参考计数,但是在调用endl将其释放一次,因此关键部分仍处于锁定状态。

将线程ID存储在您的类中以知道该线程拥有锁。输入关键部分并比较线程ID。相等->这不是第一个锁,因此您需要调用LeaveCriticalSection否则更新线程ID变量。

class MyStream {
...
     void Lock() {
         WaitForCriticalSection(&m_lock);
     }
     DWORD  m_EventOwner;
};

编辑

从原始答案更改了我的解决方案的机制。

这是工作代码:

namespace mynamespace {

class MyStream {
    public:
        MyStream(void) {
            InitializeCriticalSection(&m_lock);
        }

        ~MyStream(void) {
            DeleteCriticalSection(&m_lock);
        }


        template <typename T>
        MyStream& operator<<(const T& x) {
            Lock();
            std::cout << x;

            return *this;
        }

        void Lock() {
            EnterCriticalSection(&m_lock);
            if (m_eventOwner == GetCurrentThreadId()) {
                LeaveCriticalSection(&m_lock);
            }
        }

        void Unlock() {
            if (m_eventOwner != GetCurrentThreadId()) {
                //error!
            }
            LeaveCriticalSection(&m_lock);
        }

        MyStream& operator<<(MyStream& (*endl)(MyStream&)) {
            return endl(*this);
        }

        DWORD  m_eventOwner;
        CRITICAL_SECTION m_lock;
    };

    static MyStream& endl(MyStream& stream) {
        std::cout << std::endl;
        stream.Unlock();
        return stream;
    }

    MyStream stream;

};

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

MySQL - 多线程更新(没有线程更新相同的 id)但有死锁

来自分类Dev

线程构建块:死锁,因为所有线程都用完了

来自分类Dev

线程安全队列死锁

来自分类Dev

具有等待和通知的3个线程的死锁行为

来自分类Dev

c3p0 DataSource监视器死锁-所有线程都挂起-如何修复

来自分类Dev

模拟Java线程死锁

来自分类Dev

线程死锁和同步

来自分类Dev

模拟Java线程死锁

来自分类Dev

避免线程死锁

来自分类Dev

Java,线程死锁?

来自分类Dev

可选的线程安全性(仅在特定情况下具有线程安全性部分)

来自分类Dev

具有线程安全队列的C ++ 11事件循环

来自分类Dev

使用foo [bar] = baz添加到ConcurrentDictionary是否具有线程安全性?

来自分类Dev

我是否有在C ++中使用std :: condition_variable将线程置于死锁的风险?

来自分类Dev

具有多处理模块的死锁

来自分类Dev

避免单线程死锁

来自分类Dev

释放线程时死锁

来自分类Dev

多线程的MS SQL死锁

来自分类Dev

避免单线程死锁

来自分类Dev

多线程中的Java死锁

来自分类Dev

当可能有迭代器时,替换并发集合是否具有线程安全性?

来自分类Dev

仅在构造函数中使用私有设置器会使对象具有线程安全性吗?

来自分类Dev

使缓存的计算结果具有线程安全性的最有效方法是什么?

来自分类Dev

将SQLAlchemy与引擎/连接而不是会话一起使用是否具有线程安全性?

来自分类Dev

std :: cout << std :: cin有什么作用?

来自分类Dev

异步等待没有死锁

来自分类Dev

与调度有关的死锁

来自分类Dev

std :: cout是否被缓冲?

来自分类Dev

std::cout 的奇怪行为