2

我对信号量实现方案很感兴趣,我了解到在x86中,我们可以使用“锁定前缀”来实现原子操作,我想用它来实现互斥锁,我知道C++ 11现在有标准互斥锁,但我想实现我自己的。这是我的代码:

#include <iostream>
#include <thread>
#include <vector>

struct Semaphore
{
private:
    int s;
public:
    Semaphore( int s ) : s(s){}
    void wait()
    {
        int *p = &s;
        _asm
        {
            mov eax, p
            lock dec DWORD PTR [eax]
    begin : mov ebx, DWORD PTR [eax]
            cmp ebx, 0
            jl begin
        };
    }

    void signal()
    {
        int *p = &s;
        _asm
        {
            mov eax, p
            lock inc DWORD PTR [eax]
        };
    }
} s(1);

void print()
{
    s.wait();
    std::cout << "Print Thread " << std::this_thread::get_id() << std::endl;
    s.signal();
}

int main( int argc, char* argv )
{
    std::vector< std::thread > vec;
    int n = 3;
    for( int i = 0; i < n; ++i ) vec.push_back( std::thread( print ) );
    for( int i = 0; i < n; ++i ) vec[i].join();

    return 0;
}

问题是当有两个线程时,代码运行良好,而在 3 个线程的情况下,程序似乎陷入死锁状态,谁能解释原因或给我一些关于如何在 x86 机器上实现的建议?

4

4 回答 4

5

wait真的是一个自旋锁——当锁处于争用状态时,它将(尝试)使用 100% 的 CPU,直到其他线程释放信号量。不幸的是,由于它使用了 100% 的 CPU,这会阻止其他线程获得 CPU 时间,所以你会得到一些接近死锁的东西。

猜测一下,您可能在双核 CPU 上运行。在这种情况下,其他线程可以全速运行,即使自旋锁处于紧密循环中,浪费 CPU 时间。当您获得的线程数超过可用的 CPU 内核时,事情就会停止。

如果您有充分的理由相信信号量会很快被清除(在这种情况下您希望避免任务切换的开销),自旋锁可能很有用。但是,在典型情况下,您希望限制“旋转”所花费的时间,因此您的循环看起来像:

        mov ecx, 100
begin : mov ebx, DWORD PTR [eax]
        test ebx, ebx
        loopnz begin

然后,在它跳出循环后,您检查信号量是否已清除,或者您的限制(在本例中为 100 次迭代)是否已达到。如果达到限制,则调用调度程序让其他线程运行(并在该线程再次运行时重试等待)。

于 2013-08-14T15:40:24.547 回答
2

您创建的代码不是信号量的正确实现。信号量应该是把等待任务放到等待队列中等待信号量;之后,它的代码不会运行,直到信号量再次发出信号;当信号量发出信号时,一个等待线程被唤醒。一半的信号量代码在内核中,如何访问它的细节在线程库实现中。因此,要在 C++ 中正确实现信号量,您需要做一些更复杂的事情。或者您可以编写自己的操作系统。

此外,您没有说您使用的是什么编译器,但您的编译器可能过于激进地优化 asm 子句。

于 2013-08-14T15:28:16.910 回答
2

这里有多个问题。这里有两个。

  1. 您的 wait() 例程无条件地递减计数器。如果您有两个服务员,那么您的计数将为 -2,并且您需要两个信号才能让任何服务员停止等待。

  2. 所写的信号量代码完全依赖于调度程序。因此,根据调度程序以及等待者和信号者的优先级,等待任务(它们是繁忙的循环)完全有可能永远不会屈服于另一个执行上下文。

希望这可以帮助。

于 2013-08-14T15:50:31.217 回答
0

是的,我承认没有操作系统或机器的帮助我无法实现自己的版本,所以我尝试使用 C++11 标准来实现一个,我发现斯坦福的一个课程给出了一个解决方案,我想与任何需要它的人分享,这里是链接:http://www.stanford.edu/class/cs110/lectures/18-threading-and-semaphores.html#(3)

于 2013-08-15T08:05:38.917 回答