无锁固定大小队列失败

fail with lock-free fixedsize queue

我尝试用D语言实现类似无锁固定大小队列的东西

import core.atomic;
struct Chunk(T, uint N)
{
    T[N] data;
    shared uint count_;
    shared uint queueCounter;

    @property bool full() { return count_ == N; }

    void append(T value)
    {
        atomicOp!("+=")(queueCounter, 1);
        while(1)
        {
            uint c = count_;
            if(cas(&count_, c, c + 1))
            {
                data[c] = value;
                atomicOp!("-=")(queueCounter, 1);
                break;
            }
        }       
    }

    bool wait()
    {
        if(!full())
        {
            return false;
        }

        while(0 != queueCounter) {}

        return true;
    }
}

这样称呼它:

import std.parallelism;

struct S
{
    bool dirty;
    int time;
    int[16] data;
}

int main(string[] argv)
{
    const uint N = 14343;

    Chunk!(S, N) ch;


    foreach(i; taskPool.parallel(std.range.iota(N), 10))
    {
        S item;
        item.time = i;
        ch.append(item);
    }
    while(!ch.wait()) {}

    // DONE

    return 0;
}

它在 N == 14343 上工作正常,但在没有任何消息的情况下失败 14344(值取决于 S.sizeof)。

为什么程序会失败?

我的 CAS 追加是否正确?

在 "DONE" 字符串之后 chunk.data 是否可以完全访问?

看起来您在 Windows 上 运行,默认堆栈大小为 1 MB(至少根据 this article at MSDN)。

你的 S.sizeof 可能是 72,它给出的 S 不超过 14563 个实例(堆栈上还有其他东西,因此你的最大值 N 略低) .

在堆栈上放置一个较大的变量会导致堆栈溢出,这应该在调用 main 时立即发生:然后将 ch 的值赋给 Chunk!(S, N).init,这会导致写入堆栈边界之外,击中保护页并因此导致程序因分段错误而崩溃(至少这是 Linux 上的结果,当 N 大到足以溢出默认的 8 兆字节堆栈时),或者,在 Windows 术语,访问冲突(我现在没有 Windows 框来验证它)。

有几个解决方案:

  1. 使用较小的 N.
  2. 增加堆栈大小(参见上面链接的文章)。
  3. 在堆上分配 data