跨平台条件变量问题

时间:2013-06-13 12:44:05

标签: c++ multithreading cross-platform deadlock

在我们的项目中,我们需要为每个操作系统(linux,android,osx和win)实现os 依赖抽象(如文件,套接字,线程...)。 实现os抽象的一般方法是:

  1. 编写一个os 独立的类头,它定义了每个操作系统(Cond)常用的方法。
  2. 使用os函数编写一个os 依赖实现(例如pthread_cond,win32 ConditionVariable)。
  3. 通过cmake链接和构建。
  4. 我在Windows中遇到了实现条件变量的问题。 我需要在我的Mutex类中包装CriticalSection函数,因为Windows中的ConditionVariable适用于关键部分。当然,我在posix系统上使用标准posix解决方案。

    这是代码:

    1.a)Header(linux) - Cond.h

    #pragma once
    
    #include <pthread.h>
    #include "Types.h"
    #include "Mutex.h"
    #include "OS.h"
    
    Class Cond
    {
    public:
    
                    Cond();
                    ~Cond();
    
        void        signal();
        void        wait(Mutex& mutex);
    
    private:
    
        pthread_cond_t  m_cond;
    };
    

    1.b)标题(获胜) - Cond.h

    #pragam once
    
    #include <windows.h>
    #include "Types.h"
    #include "Mutex.h"
    #include "OS.h"
    
    class Cond
    {
    public:
                        Cond();
                        ~Cond();
    
        void            signal();
        void            wait(Mutex& mutex);
    
    private:
    
        CONDITION_VARIABLE      m_cond;
    };
    

    如您所见,界面很常见,但类型不同。

    2.a)实施(linux) - Cond.cpp

    #include "Cond.h"
    
    //-----------------------------------------------------------------------------
    Cond::Cond()
    {
        memset(&m_cond, 0, sizeof(pthread_cond_t));
    
        pthread_cond_init(&m_cond, NULL);
    }
    
    //-----------------------------------------------------------------------------
    Cond::~Cond()
    {
        pthread_cond_destroy(&m_cond);
    }
    
    //-----------------------------------------------------------------------------
    void Cond::signal()
    {
        pthread_cond_signal(&m_cond);
    }
    
    //-----------------------------------------------------------------------------
    void Cond::wait(Mutex& mutex)
    {
        pthread_cond_wait(&m_cond, &(mutex.m_mutex));
    }
    

    linux条件变量的实现效果很好!

    2.b)实施(获胜) - Cond.cpp

    #include "Cond.h"
    
    Cond::Cond()
    {
        InitializeConditionVariable(&m_cond);
    }
    
    Cond::~Cond()
    {
    
    }
    
    void Cond::signal()
    {
        WakeConditionVariable(&m_cond);
    }
    
    void Cond::wait(Mutex& mutex)
    {
        CRITICAL_SECTION cs = mutex.handle(); // returns CRITICAL_SECTION
    
        SleepConditionVariableCS(&m_cond, &cs, INFINITE);
    }
    

    此实现编译但不起作用(死锁)。

    这是一个适用于Linux的示例,但不适用于Win:

    void ResourceManager::flush()
    {
        check_load_queue();
    
        while (true)
        {
            // Wait for all the resources to be loaded
            // by the background thread
            m_loading_mutex.lock(); 
            while (m_loading_queue.size() > 0)
            {
                m_all_loaded.wait(m_loading_mutex);  //Cond
            }
            m_loading_mutex.unlock();
    
            // When all loaded, bring them online
            bring_loaded_online();
    
            return;
        }
    }
    

    修改 Windows Critical Section Wrapper - Mutex.h&amp; Mutex.cpp

    #pragma once
    
    #include <windows.h>
    #include "Types.h"
    #include "OS.h"
    
    
    class Mutex
    {
    public:
    
                            Mutex();
                            ~Mutex();
    
        void                lock();
        void                unlock();
    
        CRITICAL_SECTION    handle();
    
    private:
    
        CRITICAL_SECTION    m_cs;
    
        friend class        Cond;
    };
    

    #include "Mutex.h"
    
    namespace crown
    {
    namespace os
    {
    
    //-----------------------------------------------------------------------------
    Mutex::Mutex()
    {
        InitializeCriticalSection(&m_cs);
    }
    
    //-----------------------------------------------------------------------------
    Mutex::~Mutex()
    {
        DeleteCriticalSection(&m_cs);
    }
    
    //-----------------------------------------------------------------------------
    void Mutex::lock()
    {
        TryEnterCriticalSection(&m_cs); 
    }
    
    //-----------------------------------------------------------------------------
    void Mutex::unlock()
    {
        LeaveCriticalSection(&m_cs);
    }
    
    //-----------------------------------------------------------------------------
    CRITICAL_SECTION Mutex::handle()
    {
        return m_cs;
    }
    
    你可以解释一下为什么吗?解决方案? 谢谢!

2 个答案:

答案 0 :(得分:2)

为什么呢?不知道。解决方案:使用可移植且免费提供的库来包装平台依赖项。

  • std::thread用于多线程:#include <thread>
  • Boost.Asio用于联网:#include <boost/asio.hpp>
  • Boost.FileSystem适用于文件系统:#include <boost/filesystem.hpp>

Boost库通常是标准库的前导码。几乎所有的线程功能都可以在最新的C ++ 11标准版之前几年Boost.Thread中获得。预计Boost.Asio和Boost.Filesystem也将构建标准化模块。

要让它在CMake中工作,install Boost并使用类似

的内容
find_package(Boost COMPONENTS thread asio filesystem REQUIRED)

答案 1 :(得分:1)

这当然是错的:

CRITICAL_SECTION Mutex::handle()
{
    return m_cs;
}

您将返回CRITICAL_SECTION的副本,即cannot do。改为返回引用或指针。

如评论中所述,您还应该在Mutex和Cond类中实现私有拷贝构造函数和赋值运算符,因为平台相关的互斥锁(例如pthread_mutex_t)和条件变量无法复制 - 这样做会导致使用时出现未定义的行为这些对象