其他分享
首页 > 其他分享> > c – 为什么我的无锁消息队列是segfault :(?

c – 为什么我的无锁消息队列是segfault :(?

作者:互联网

作为一个纯粹的心理练习,我试图让它在没有锁或互斥体的情况下工作.这个想法是当消费者线程正在读取/执行消息时,它以原子方式交换生产者线程用于写入的std :: vector.这可能吗?我试过玩线程围栏无济于事.这里有竞争条件,因为它偶尔会出现故障.我想它在enqueue函数中的某个地方.有任何想法吗?

// should execute functions on the original thread
class message_queue {
public:
    using fn = std::function<void()>;
    using queue = std::vector<fn>;

    message_queue() : write_index(0) {
    }

    // should only be called from consumer thread
    void run () {
        // atomically gets the current pending queue and switches it with the other one
        // for example if we're writing to queues[0], we grab a reference to queue[0]
        // and tell the producer to write to queues[1]
        queue& active = queues[write_index.fetch_xor(1)];
        // skip if we don't have any messages
        if (active.size() == 0) return;
        // run all messages/callbacks
        for (auto fn : active) {
            fn();
        }
        // clear the active queue so it can be re-used
        active.clear();
        // swap active and pending threads
        write_index.fetch_xor(1);
    }
    void enqueue (fn value) {
        // loads the current pending queue and append some work
        queues[write_index.load()].push_back(value);
    }
private:
    queue queues[2];
    std::atomic<bool> is_empty; // unused for now
    std::atomic<int> write_index;


};
int main(int argc, const char * argv[])
{

    message_queue queue{};
    // flag to stop the message loop
    // doesn't actually need to be atomic because it's only read/wrote on the main thread
    std::atomic<bool> done(false);
    std::thread worker([&queue, &done] {
        int count = 100;
        // send 100 messages
        while (--count) {
            queue.enqueue([count] {
                // should be executed in the main thread
                std::cout << count << "\n";
            });
        }
        // finally tell the main thread we're done
        queue.enqueue([&] {
            std::cout << "done!\n";
            done = true;
        });
    });
    // run messages until the done flag is set
    while(!done) queue.run();
    worker.join();
}

解决方法:

您的无锁队列无法正常工作,因为您没有从至少一个半正式的正确性证明开始,然后将该证明转换为一个算法,其中证明是主要文本,注释将证明连接到代码,所有这些都与代码.

除非您复制/粘贴其他人的实现,否则任何尝试编写无锁算法都将失败.如果您正在复制粘贴其他人的实施,请提供.

除非你有这样的证据证明它们是正确的,否则无锁算法并不健全,因为使它们失败的错误类型是微妙的,必须格外小心.简单地“滚动”无锁算法,即使它在测试期间未能导致明显的问题,也是不可靠代码的处方.

在这种情况下编写正式证明的一种方法是追踪已经证明正确的伪代码等的人.在评论中勾画出伪代码以及正确性证明.然后填写孔中的代码.

一般来说,证明一个“几乎正确”的无锁算法存在缺陷比写一个无锁算法的正确证据更难,如果以特定方式实现,然后实现它.现在,如果您的算法存在缺陷,很容易找到缺陷,那么您就不会对问题域有基本的了解.

简而言之,通过发布“为什么我的算法错误”,您正在接近如何错误地编写无锁算法. “我的证明中的缺陷在哪里?”,“我在这里证明了这个伪代码是正确的,然后我实现了它,为什么我的测试显示出死锁?”是很好的无锁问题. “这里有一堆带有注释的代码,仅仅描述了下一行代码的作用,没有描述我为什么要编写下一行代码的注释,或者这行代码如何保持我的无锁不变量”不是一个好的无锁问题.

退后.找到一些经过验证的正确算法.了解证明如何运作.通过猴子实现一些经过验证的正确算法 – 参见monkey-do.查看脚注,注意他们的证据被忽略的问题(如A-B问题).在您掌握了一堆之后,尝试一个变体,并进行校对,检查证明,并执行实施,并检查实施情况.

标签:lock-free,c,c11,multithreading,atomic
来源: https://codeday.me/bug/20190728/1563284.html