【问题标题】:c++ implementing semaphore on my ownc ++自己实现信号量
【发布时间】:2014-09-10 09:30:07
【问题描述】:

让我们假设没有为 C++ 提供信号量的库。我是这样写的:

#include <vector>
#include <Windows.h>

class Semaphore {
    HANDLE mutexS;                  //  provides mutex in semaphore rutines
    std::vector<HANDLE> queue;      //  provides FIFO queue for blocked threads
    int value;                      //  semaphore's value

public:
    Semaphore(int init=1);  
    ~Semaphore();

    void wait();
    void signal();
};


Semaphore::Semaphore(int init) {
    value = init;
    queue = std::vector<HANDLE>();
    mutexS = CreateMutex(0,0,0);
}

Semaphore::~Semaphore() {
    CloseHandle(mutexS);
}




void Semaphore::signal() {
    WaitForSingleObject(mutexS, INFINITE);
    if (++value <= 0) {
        HANDLE someOldThread = queue.front();
        ResumeThread(someOldThread);
        queue.erase(queue.begin());
        CloseHandle(someOldThread);
    }
    ReleaseMutex(mutexS);
}

我想知道为什么这个 wait() 的实现不起作用:

void Semaphore::wait() {
    WaitForSingleObject(mutexS, INFINITE);

    if (--value < 0) {
        HANDLE thisThread = GetCurrentThread();   
        queue.push_back(thisThread);
        ReleaseMutex(mutexS);
        SuspendThread(thisThread );
    }
    else
        ReleaseMutex(mutexS);
}

这一个有效:

void Semaphore::wait() {
    WaitForSingleObject(mutexS, INFINITE);

    if (--value < 0) {
        HANDLE thisThread = GetCurrentThread();
        HANDLE alsoThisThread;

        DuplicateHandle(GetCurrentProcess(), thisThread, GetCurrentProcess(), &alsoThisThread, 0, 0, DUPLICATE_SAME_ACCESS);

        queue.push_back(alsoThisThread);

        ReleaseMutex(mutexS);

        SuspendThread(alsoThisThread);
    }
    else
        ReleaseMutex(mutexS);
}

每种情况下究竟发生了什么?很长时间以来,我一直在努力解决它。等待的第一个实现,它不起作用,使我的程序阻塞(嗯,它可能永远阻塞一些线程)。第二个实现就像一个魅力。是什么赋予了 ?为什么我需要复制线程句柄并阻止重复?

【问题讨论】:

  • 可能是因为你在signal()的实现中调用了CloseHandle?我不太精通 WinAPI,但似乎只在线程完成时关闭线程才明智,而不是在它应该恢复时关闭。
  • 你也应该稍后访问codereview,因为你的代码肯定可以改进很多。
  • 否 - 如果我不 CloseHanlde(sameOldThread);结果是一样的。不是这样的:(谢谢
  • 不是直接回答您的问题,但可以使用计数器、互斥体和条件变量 (msdn.microsoft.com/en-us/library/windows/desktop/…) 制作信号量。
  • 如果您想要一个 C++ 信号量(假设尚不存在),为什么不在 Windows 信号量周围加上一个包装器呢?您已经在使用 Windows 互斥量,因此如果您想要一个真正的信号量,已经提供了一个。我问这个以防你编写代码不是为了有趣和实验。如果没有,请忽略我。

标签: c++ multithreading winapi semaphore


【解决方案1】:

MSDN 在这里有很大帮助 ;)

GetCurrentThread 返回一个伪句柄,它是“当前线程”的常量:

伪句柄是一个特殊的常量,被解释为当前线程句柄。

因此,当您将其推送到队列中时,您总是推送一个表示“当前线程”的常量,这显然不是您想要的。

要获得真正的句柄,你必须使用DuplicateHandle

如果 hSourceHandle 是 GetCurrentProcess 或 GetCurrentThread 返回的伪句柄,DuplicateHandle 会将其分别转换为进程或线程的真实句柄。

最后一点:我想你是把它作为一个“测试”来实现的,对吧?因为有几个潜在的问题。一个很好的学习练习就是把它们挖出来。但是你不应该在生产代码中使用它。

出于好奇:如果您想进行更多实验,使用互斥锁实现信号量的“规范”方式是使用两个互斥锁:see here

【讨论】:

  • 我阅读了两遍文档。我想我累了...谢谢!这是解决其他一些问题的方法。你能指出你看到的缺陷吗?我想我再也不会写这样的东西了——但我想知道。谢谢!
  • stefaanv 提供的链接(或我链接的其他实现)是很好的起点;我个人不太喜欢手动暂停线程/恢复线程逻辑。另一个线程可以通过调用函数“恢复”休眠线程(它所要做的就是获取句柄)
  • 你也可以尝试让它递归:如果一个线程两次获取相同的信号量怎么办? (通常,第二次获取成功)。
  • 我明白了 - 所以它对滥用很敏感。它不会在我的解决方案中造成问题(它是一个学校项目,没有人会滥用它:D) - 但很高兴知道这一点。感谢您的帮助!
  • 没问题 ;) 您可能还想看看 MSDN mag (msdn.microsoft.com/en-us/magazine/cc163642.aspx) 上的一篇不错的文章(基于互斥锁的信号量通常需要一个公平的互斥锁,而 Win32 互斥锁并不完全公平)
【解决方案2】:

MSDN documentation for GetCurrentThread 有答案(口音是我的):

返回值是当前线程的伪句柄

伪句柄是解释为当前线程句柄的特殊常量。每当需要线程句柄时,调用线程可以使用此句柄来指定自己。

...

该函数不能被一个线程使用来创建一个句柄,该句柄可以被其他线程用来引用第一个线程。 句柄总是被解释为引用正在使用它的线程。通过在调用 DuplicateHandle 函数时将伪句柄指定为源句柄,线程可以为自己创建一个“真实”句柄,供其他线程使用或由其他进程继承。

【讨论】:

  • 我阅读了两遍文档。我想我累了...谢谢!
最近更新 更多