具有长链表的多线程

时间:2013-05-23 10:04:26

标签: c multithreading algorithm

我这里有一个算法问题。系统中有10个线程,您将获得一个包含10 K个元素的链接列表。如何进行线程同步(添加删除等)以便对性能进行优化?不建议在列表中使用互斥锁,因为它会降低性能。

7 个答案:

答案 0 :(得分:2)

如果以相同的频率访问所有位置并且您可以修改列表节点,则可以为每个节点添加互斥锁:

typedef struct node{ 
   char* data;
   struct listNode *next;
   pthread_mutex_t lock; 
} listNode ;

还取决于节点数据的大小。如果它非常小,由于互斥锁存储,创建和删除,这可能会导致开销。

如果是开销或无法修改节点,您可以将列表拆分为(例如)100组100个元素,并为每个组使用互斥锁

答案 1 :(得分:1)

链表数据结构假设所有操作都遵循顺序规则。看看concurrent linked list

  

无论你使用什么样的机器来实现它,   接口和预期行为意味着顺序逻辑。

答案 2 :(得分:0)

您可以使用Linux系统调用futex进行同步。

答案 3 :(得分:0)

这取决于您要对链接列表执行的操作以及列表是否已排序。

  1. 如果您担心2个线程会更改某个节点的值,请为每个内核添加互斥锁,如此处所述。
  2. 如果你关心列表操作(添加,删除):它取决于你做多读取而不是写 - 使用读写器锁定,如果每个线程正在处理列表的一部分而不是你只能删除访问相关的线程

答案 4 :(得分:0)

我发现answer of Evans是正确的。但我建议使用 spinlocks 而不是互斥锁。在低并发性以及持有锁的时间短的情况下,自旋锁更有效。

typedef 
struct ListNode { 
   void * data;
   struct ListNode * next;
   pthread_spinlock_t lock;
}
ListNode;

答案 5 :(得分:0)

正确的解决方案在很大程度上取决于您想要同步的对象的操作频率(在您的情况下为列表)。容器的操作是容器创建,容器修改,容器遍历和项目修改。例如,如果列表主要是遍历和读取,则可能是列表是作业的错误容器。也许你真的需要某种地图(也称为字典),如果你有一个键值,它可以提供真正快速的读取访问。在这种情况下,根本没有遍历,可能是整个地图容器的同步变得最有效,仅仅是因为我们改变了容器的类型。

答案 6 :(得分:0)

首先假设向列表中添加/删除元素不是多线程的原因(而是确定/创建这些元素的逻辑是征税过程)..如果列表插入/删除时间是瓶颈然后你可能应该重新考虑你的数据结构。

接下来,假设每个线程不会相互交互(一个线程不会删除另一个线程插入的记录),并且每个线程的工作量都是有限的。让每个线程都不触及实际的链表,而是让每个线程都保留两个补充列表。

它的工作原理如下:

  1. 每个线程运行并创建两个要删除和插入的记录的补充列表

  2. 如果线程全部完成后线程未排序,我们可以将每个线程的“补充新项目”列表附加到现有列表的开头或结尾。

  3. 接下来对于已删除的项目,我们合并要从每个线程中删除的项目列表,然后我们遍历原始链接列表并在遇到它们时删除这些项目(通过使用{{1要删除的项目列表。

  4. 如果开始时的两个假设成立,则效果非常好。此外,它意味着不需要互斥锁或锁,只有在所有线程全部连接回主线程后,主列表才会在最后由单个线程更新。