【问题标题】:Multi-threaded lock-free doubly linked list using free-list使用free-list的多线程无锁双向链表
【发布时间】:2019-11-01 19:00:13
【问题描述】:

我想要一个像单链表一样工作的并发数据结构,只需要appendremove_iterator 操作。最后,一个线程将iterate所有节点。根据我的研究,我得到了一个 implementation,它具有带有单链表的 appendremove_valuesearch_value 操作。它基于Harris' algorithm

问题在于,在 Harris 算法中,remove_value 仅将节点标记为逻辑删除,而没有实际删除它。 search_value 实际上负责删除逻辑删除的节点。但是由于我的用例没有search 操作。我保留了一个很长的列表,其中包含许多逻辑删除的节点。只是在多线程中效率不高,因为删除节点的所有工作都放在单个线程中的iterate 操作上。

我想知道是否还有其他适合我需要的实现。任何建议表示赞赏。

如果不是,我想知道我是否可以使用带有无锁堆栈实现的free-list 来为这种特殊情况实现一些东西。在这种情况下,append 操作变为pop 空闲列表,将值放入节点或如果为空则附加到我们的列表中。 remove_iterator 操作变为标记逻辑上删除的节点,push 成为指向空闲列表的节点指针。

我认为无锁堆栈很容易实现。我可以在线使用一些implementations


记住一些代码。

struct node_t {
    node_t *next;
    int deleted;
    val_t val;
};
struct list_t {
    node_t *head;
};
struct fl_node_t {
    node_t *padding_1;
    int padding_2;
    fl_node_t *next; // assume sizeof(val_t) >= sizeof(fl_node_t*);
};

struct free_list_t {
    fl_node_t * head;
};
void append(val_t val) {
    fl_node_t *fl_head;
    fl_node_t *fl_next;
    node_t *head;
    node_t *new_node
    /* Try insert to one of the node in free-list */
    if (free_list.head) {
        do {
            fl_head = free_list.head;
            next = fl_head->next;
        } while(!CAS(&free_list.head, fl_head, fl_next)); 
        if (fl_head) {
           fl_head->node->val = val; 
           return;
        }
    }
    /* Append to head */
    new_node = malloc(sizeof(node_t));
    new_node.val = val;
    new_node.deleted = 0;
    do {
        head = list.head;
        new_node.next = head;
    } while(!CAS(&list.head, head, new_node));

}
void remove(node_t *node) {
    fl_node_t *fl_node;
    fl_node_t *fl_head;

    /* Mark logically deleted */
    node->deleted = 1;
    fl_node = (fl_node_t*) node;

    /* Add to free-list */
    do {
        fl_head = free_list.head;
        fl_node->next =fl_head;
    } while(!CAS(&free_list.head, fl_head, fl_node)); 
}

【问题讨论】:

  • 需要无锁的条件是什么?
  • 许多线程同时处理同一个数据结构。非无锁作品。我有一个列表的全局锁定。表现不是很好。我绝对可以为每个节点切换到细粒度的锁定。但是,它可能会带来大量内存开销。互斥体是 0x40 字节,而我的 val_t 只是 0x8 字节。而且名单很大。
  • 对于一些小的固定 N 和随机着色节点有 N 个锁怎么样?我不确定你是否可以用它来制定一个安全的锁定顺序协议,但这听起来很合理。
  • 你的意思是以下。而不是一份清单。我可以使用 N 个列表和 N 个锁。并将值随机添加到列表之一。更新时,只需锁定正确的锁即可。这样,就不需要锁定顺序。我想我可以尝试使用新列表的变体,并在添加 N 个值时锁定。
  • 听起来你需要三个操作:前向迭代、追加和remove_iterator。其中一些仅由一个线程执行吗?例如你有一个线程修改和一个线程阅读?还是一篇写多读?了解此类限制有助于找到解决方案。

标签: c multithreading atomic lock-free compare-and-swap


【解决方案1】:

当涉及无锁和无等待数据结构时,与其尝试重新发明轮子或修复某些东西并花费大量时间来证明它是正确的,不如尽可能使用经过验证的现有实现

众所周知,无锁实现很难正确,也很难证明正确。在一种 CPU 架构上是正确的,在另一种 CPU 架构上可能是错误的。

在实践中,出于性能原因,我们不得不使用它们,它们可能会不停地使用,然后有一天会爆炸。我们从这里改编的实现取得了很好的成功

http://www.1024cores.net

他还引用了其他库,并提供了对并发编程的深入了解。值得一读。

【讨论】:

  • 非常感谢您的建议。正如您所说,很难证明无锁算法的正确性。没有适合我需要的实现。因此,我使用了两个经过充分研究的实现:T. Harris 的单链表和无锁堆栈。虽然我的数据结构没有正式的书面证明,但它通过了@gavra0 在 github 上实现的测试用例。我对我所做的实施相当有信心。
【解决方案2】:

我在github 上找到了 gavra0 的实现。通过添加一个空闲列表(使用无锁堆栈实现)进行修改,我得到了一些工作代码。

存储库是https://github.com/buszk/ConcLinkedList。而实现就在link这个dev分支中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 2016-07-01
    • 1970-01-01
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多