【问题标题】:Win32 alternative to pthreadpthread 的 Win32 替代方案
【发布时间】:2014-01-21 06:48:32
【问题描述】:

是否可以使用标准的 win32 CreateMutex 样式代码编写此代码。我只是想知道我是否想在我们的应用程序中引入一个新库,或者我是否可以找到一种自己编写的方法。我只是不知道如何在关键部分中等待。这是我当前使用 pthread 库的工作代码。

T remove() {
    pthread_mutex_lock(&m_mutex);
    while (m_queue.size() == 0) {
        pthread_cond_wait(&m_condv, &m_mutex);
    }
    T item = m_queue.front();
    m_queue.pop_front();
    pthread_mutex_unlock(&m_mutex);
    return item;
}

【问题讨论】:

  • 老实说,我会使用 C++11 中的 thread support library,VC++2012 及更高版本支持。如果您必须自己动手,那么诚实地使用 Windows 原语执行此操作是不值得的。如果您可以将您的安装域限制为 Vista/Server2008 及更高版本,则可以使用Windows Condition Variables,使其更加直接。
  • 仔细考虑 WhozCraig 的建议。在 pre-Vista Win32 上滚动您自己的条件变量支持并非易事:cs.wustl.edu/~schmidt/win32-cv-1.html
  • 我坚持支持 MSVC 6.0 和 MSVC 2010,所以它不是一个选项。但是,我可以使用 pthread 库,但想确保我没有错过一些简单的替代方案。我不是你的cmets。
  • 风格说明:避免使用size() == 0 测试空虚;并非所有容器都提供 O(1) size(),这就是为什么有 empty() - 它不 [总是] 只是一个捷径。另外,在我看到您不能使用 C++11 之前,我将您的代码转换为 std::thread,请参见此处:pastebin.com/aVzJL1ag

标签: c++ winapi pthreads win32-process


【解决方案1】:

对于 VC-2012 之前的支持,最好的替代方案是支持条件变量的 Boost.Thread

【讨论】:

  • 我想是时候学习 Boost 了……但也许以后……再加上 Boost.Thread 仍然是一个额外的 dll,这是我试图避免的。
  • 在您要求它存在之前,它不在附加 dll 中。默认情况下(至少在 Windows 上)它被编译为静态库。是的,它仍然应该编译,但这需要 5 分钟。
  • 好的,谢谢。不知道那个。真的要找时间驯服Boost兽了……
【解决方案2】:

这是我的尝试。这不是 win32 中条件等待锁的最佳实现,但我认为它有效。它可以使用仔细的代码审查审查。

一个警告 - 它不一定保证有序的公平性,因为所有等待的线程最初可能会被阻塞以等待事件。此时调度程序将恢复所有线程以继续运行(直到随后的阻塞 EnterCriticalSection 调用),但不一定按照线程到达 remove() 调用开始时的相同顺序。对于大多数只有少数线程的应用来说,这可能没什么大不了的,但这是大多数线程框架所保证的。

其他警告 - 为简洁起见,我省略了检查所有这些 Win32 API 的返回值的重要步骤。

CRITICAL_SECTION m_cs;
HANDLE m_event;

void Init()
{
   InitializeCriticalSection(&m_cs);
   m_event = CreateEvent(NULL, TRUE, FALSE, NULL); // manual reset event
}

void UnInit()
{
    DeleteCriticalSection(&m_cs);
    CloseHandle(m_event);
    m_event = NULL;
}

T remove()
{
    T item;
    bool fGotItem = false;
    while (fGotItem == false)
    {
        // wait for event to be signaled
        WaitForSingleObject(m_event, INFINITE);

        // wait for mutex to become available
        EnterCriticalSection(&m_cs);

        // inside critical section
        {
            // try to deque something - it’s possible that the queue is empty because another 
            // thread pre-empted us and got the last item in the queue before us

            size_t queue_size = m_queue.size();

            if (queue_size == 1)
            {
               // the queue is about to go empty
               ResetEvent(m_event);
            }

            if (queue_size > 0)
            {
                fGotItem = true;
                item = m_queue.front();

                m_queue.pop();        
            }
        }

        LeaveCriticalSection(&m_cs);

    }

    return item;
}

void Add(T& item)
{
    // wait for critical section to become available
    EnterCriticalSection(&m_cs);

    // inside critical section
    {
        m_queue.push_back(item);

        SetEvent(m_event); // signal other threads that something is available
    }

    LeaveCriticalSection(&m_cs);
}

【讨论】:

  • 谢谢,这对我来说是最好的答案。适用于 MSVC6.0 和 2012、Win7 和 WinXp!
  • @uncletall - 只要确保您对其进行了适当的测试,并至少对 Init 和 WaitForSingleObject 调用中的调用的返回值进行一些验证。我实际上并没有编译或测试这段代码。我实际上是用我的 Mac-Mini 上的文本编辑器编写的 :)
【解决方案3】:

Windows Vista 为此类场景引入了新的原生 Win32 Conditional VariableSlim Reader/Writer Lock 原语,例如:

使用临界区:

CRITICAL_SECTION m_cs;
CONDITION_VARIABLE m_condv;

InitializeCriticalSection(&m_cs);
InitializeConditionVariable(&m_condv);

...

void add(T item)
{ 
    EnterCriticalSection(&m_cs);
    m_queue.push_back(item);
    LeaveCriticalSection(&m_cs);
    WakeConditionVariable(&m_condv);
}

T remove()
{ 
    EnterCriticalSection(&m_cs);
    while (m_queue.size() == 0)
        SleepConditionVariableCS(&m_condv, &m_cs, INFINITE);
    T item = m_queue.front();
    m_queue.pop_front();
    LeaveCriticalSection(&m_cs);
    return item;
}

使用 SRW 锁:

SRWLOCK m_lock;
CONDITION_VARIABLE m_condv;

InitializeSRWLock(&m_lock);
InitializeConditionVariable(&m_condv);

...

void add(T item)
{ 
    AcquireSRWLockExclusive(&m_lock);
    m_queue.push_back(item);
    ReleaseSRWLockExclusive(&m_lock);
    WakeConditionVariable(&m_condv);
}

T remove()
{ 
    AcquireSRWLockExclusive(&m_lock);
    while (m_queue.size() == 0)
        SleepConditionVariableSRW(&m_condv, &m_lock, INFINITE, 0);
    T item = m_queue.front();
    m_queue.pop_front();
    ReleaseSRWLockExclusive(&m_lock);
    return item;
}

【讨论】:

  • 除非 OP 需要 WinXP 支持,否则我会赞成这个答案而不是我提供的答案。我忘记了 SRW 的东西。好答案!
猜你喜欢
  • 1970-01-01
  • 2016-08-26
  • 1970-01-01
  • 2015-06-13
  • 2015-03-03
  • 2015-09-25
  • 2019-12-16
  • 2011-06-20
  • 2015-11-03
相关资源
最近更新 更多