c结构上的原子加载/保存(gcc)问题

c结构上的原子加载/保存(gcc)问题,c,gcc,struct,atomic,gcc6,C,Gcc,Struct,Atomic,Gcc6,在实现无锁单链表方面,我正在尝试我的运气 typedef _Atomic struct _node { void *data; struct _node *next; } Node; 这是否会使struct的所有成员都具有原子性 void add_head ( Linked_list* list, void* data ) { if ( debugging ) { printf ( "%s\n", __func__ ); } Node *no

在实现无锁单链表方面,我正在尝试我的运气

typedef _Atomic struct _node
  {
    void *data;
    struct _node *next;
  } Node;
这是否会使struct的所有成员都具有原子性

void add_head ( Linked_list* list, void* data )
{
  if ( debugging )
  {
      printf ( "%s\n", __func__ );
  }
  Node *node = ( Node* ) calloc ( 1, sizeof (Node ) );
  //init_node_mutex(node);
  //lock_node_mutex(node);
  atomic_exchange ( &node->next, NULL );
  atomic_exchange ( &node->data, data );

  if ( list->head == NULL )
  {
      Node* the_tail = atomic_load ( &list->tail );
      //  lock_node_mutex ( the_tail );
      atomic_exchange ( &node->next, NULL );
      atomic_compare_exchange_weak ( &list->tail, the_tail, node );

      //unlock_node_mutex ( the_tail );

  }
  else
  {

      Node* the_next = atomic_load ( &node->next );
      // lock_node_mutex ( the_next );
      atomic_compare_exchange_weak ( &node->next, the_next, list->head );
      // unlock_node_mutex ( the_next );
  }

  Node* the_head = atomic_load ( & list->head );
  //lock_node_mutex ( the_head );
  atomic_store ( &list->head, node );
  atomic_store ( &list->current, node );
  //unlock_node_mutex ( the_head );
  //unlock_node_mutex(node);
  atomic_fetch_add ( &list->size, 1 );
}

原子加载和原子存储的用法正确吗?

好的,我讨论了是将其作为“评论”还是“回答”发布,但我要在这里发言

我的直觉对我大喊大叫,“你正在执行的单个操作是否是“原子的”其实并不重要,因为你正在连续执行许多操作,以完成你最终要做的事情。即使这些单个步骤是“原子的”,整个操作也不是

“原子”操作是指使用专门的机器指令(如x86上的
LOCK
前缀或big iron上的“compare and swap”指令)执行单个操作,以确保没有其他CPU(或内核)会干扰该单个操作

但是,你不能在“一条指令”中做你想做的事情,不管是不是原子指令

因此,我诚恳地建议您现在放弃当前的课程,将那些“互斥”调用放回,并删除“原子”。您的代码(以及所有此类代码…)需要这些互斥。在我看来,您是在死路一条


(顺便说一句,“互斥”操作有时很好地利用了这些“原子指令”,因此它们可能比你可能担心的要有效得多。)

好吧,我在讨论是将其作为“注释”还是“答案”发布,但我在这里要彻底放弃

我的直觉对我大喊大叫,“你正在执行的单个操作是否是“原子的”其实并不重要,因为你正在连续执行许多操作,以完成你最终想要做的事情。即使这些单独的步骤是“原子的”,整个操作也不是

“原子”操作是指使用专门的机器指令(如x86上的
LOCK
前缀或big iron上的“compare and swap”指令)执行单个操作,以确保没有其他CPU(或内核)会干扰该单个操作

但是,你不能在“一条指令”中做你想做的事情,不管是不是原子指令

因此,我诚恳地建议您现在放弃当前课程,将那些“互斥”调用放回,并删除“原子”。您的代码(以及所有此类代码……)需要这些互斥。在我看来,你是在一条死胡同上追赶一只白兔


(顺便说一句,“互斥”操作有时很好地利用了这些“原子指令”,因此它们可能比您可能担心的要高效一些。)

除了@MikeRobinson的评论之外,我还要补充一点,虽然您的代码在某种意义上是“无锁”的,即它不包含任何对锁的明确使用,但现在是(有点讽刺的是)不再是线程安全的。编写无锁代码非常困难。我建议通读一遍以鸟瞰世界,然后阅读以获得一些细节或第7章(在C++中)。您可以随时查看源代码以获得灵感。

除了@MikeRobinson的评论,我还要补充一点,虽然您的代码是“无锁的”,即它不包含任何对锁的明确使用,但它现在是(有点讽刺意味)不再是线程安全的。编写无锁代码是非常困难的。我建议通读一遍以鸟瞰世界,然后再阅读以获得一些细节或第7章(用C++编写)。您可以随时查看源代码以获得灵感。

在C11标准中,第§6.5.2.3节“结构和工会成员”(对于
->
操作符)5表示访问原子结构或联合对象的成员会导致未定义的行为。97)例如,脚注97指出,如果对一个线程中的整个结构或联合的访问与对另一个线程中的成员的访问冲突,则会发生数据竞争,其中至少有一个访问是修改。可以使用分配给或来自原子对象的非原子对象安全地访问成员。在C11标准中,第节§6.5.2.3结构和联合成员(对于
->
运算符)5表示访问原子结构或联合对象的成员会导致未定义的行为。97)脚注97表示,如果一个线程中对整个结构或联合的访问与另一个线程中对成员的访问冲突,则会发生数据争用,其中至少有一个访问是修改。可以使用分配给或来自原子对象的非原子对象安全地访问成员。