【问题标题】:Cross-Platform Condition Variable troubles跨平台条件变量问题
【发布时间】:2013-06-13 12:44:04
【问题描述】:

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

  1. 编写一个操作系统独立类头,它定义了每个操作系统通用的方法(条件)。
  2. 使用 os 函数(例如 pthread_cond、win32 ConditionVariable)编写一个 os 依赖实现。
  3. 通过 cmake 链接和构建。

我在 Windows 中实现条件变量时遇到了问题。 我需要在我的 Mutex 类中包装 CriticalSection 函数,因为 Windows 中的 ConditionVariable 可用于关键部分。当然,我在 posix 系统上使用标准的 posix 解决方案。

代码如下:

1.a) 标头 (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 关键部分包装器 - Mutex.h 和 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;
}

你能解释一下为什么吗?解决方案? 谢谢!

【问题讨论】:

  • 添加了 win Mutex,它是一个关键部分包装器
  • 引用 MSDN:“不能移动或复制临界区对象。”
  • @nos 让 Cond 成为 Mutex 的朋友我可以做到(在 Cond::wait 中):SleepConditionVariableCS(&m_cond, &mutex.m_cs, 5000);合法吗?

标签: c++ multithreading cross-platform deadlock


【解决方案1】:

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

  • std::thread 用于多线程:#include &lt;thread&gt;
  • Boost.Asio 联网:#include &lt;boost/asio.hpp&gt;
  • Boost.FileSystem 用于文件系统:#include &lt;boost/filesystem.hpp&gt;

Boost 库通常是标准库的先驱。在最新的 C++11 标准几年前,Boost.Thread 中几乎所有的线程功能都可用。预计 Boost.Asio 和 Boost.Filesystem 也将成为标准化的基石。

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

find_package(Boost COMPONENTS thread asio filesystem REQUIRED)

【讨论】:

  • +1 表示 不要重新发明轮子 解决方案。我个人在我的项目中使用pocoproject.org(因为它有一个 VxWorks 版本)。这是一个有趣的图书馆。
  • @MatthieuRouget 我没有使用 Poco 的经验,但听起来也很有趣。 Facebook Folly 是有趣库的另一个来源。
  • std::thread 现在不是那么便携(否则我已经使用它)。此外,我们不希望向我们的项目添加更多依赖项
  • @mikymod 您将添加更少而不是更多的依赖项,因为您可以消除phtreadwin32thread,对于特定于平台的网络和文件系统也是如此。并且所有的 Boost 子库都被打包了,所以实际上只有 1 个依赖项。这就是使用标准库及其 Boost 表亲的全部意义所在。这是一项将带来丰厚回报的长期投资。
  • @mikymod 你总是可以通过使用 Boost 中的bcp 工具来提取所有需要的头文件并将它们直接集成到你的项目中来作弊:-)
【解决方案2】:

这肯定是错误的:

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

您返回的是 CRITICAL_SECTION 的副本,您 cannot do。而是返回一个引用或指针。

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

【讨论】:

  • 我可以让 Cond 成为 Mutex 的朋友(在 Cond::wait 中):SleepConditionVariableCS(&m_cond, &mutex.m_cs, 5000);合法吗?
猜你喜欢
  • 1970-01-01
  • 2018-09-28
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2010-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多