并发数据结构-1.1 并发的数据结构的设计

简介:

原文链接译文链接,译者:董明鑫,校对:周可人

随着多个处理器共享同一内存的机器在商业上的广泛使用,并发编程的艺术也产生了巨大的变化。当前的趋势向着低功耗芯片级多线程(CMT)发展,所以这样的机器一定会更加广泛的被使用。

共享内存多处理器是指并发的执行多个线程的系统,这些线程在共享的内存中通过数据结构通讯和同步。这些数据结构的效率对于性能是很关键的,而目前熟练掌握为多处理器机器设计高效数据结构这一技术的人并不多。对大多数人来说,设计并发的数据结构比设计单线程的难多了,因为并发执行的线程可能会多种方式地交错运行他们的指令,每一种方式会带来不同的,甚至不符合预期的输出。这就要求设计者改变他们对运算的认识,理解新的设计方法,采用新的编程工具集。此外,设计可扩展的并发数据结构,使得当机器执行越来越多的并发线程时依旧表现良好也是新的挑战。本文主要介绍设计并发数据结构的相关挑战,和一些重要的数据结构相关内容的总结。我们的总结绝不是全面的;相反,我们特意选取了一些能说明设计的关键问题的流行的数据结构,希望我们提供了足够的背景和知识,让有兴趣的读者接触那些我们没有提到的内容。

1.1 并发的数据结构的设计

共享内存多处理器的一些特性使得并发数据结构的设计和校验比相对应的单线程结构难度显著增加。

acquire(Lock);

oldval = X;                                                                                      oldval = X;

X = oldval + 1;                                                                                X = oldval + 1;

return oldval;                                                                                 release(Lock);

return oldval;

图1.1:顺序的和基于锁机制的fetch-and-inc操作代码片段

难点的根源在于并发:因为线程是在不同的处理器上并发的执行,而且受操作系统的调度决策、缺页、中断等等影响,我们必须按照全部异步的想法来思考,以保证不同的线程能够随意交错的运行。这显著提升了正确设计并发数据结构的复杂度。

为多处理器系统设计并发的数据结构在性能和可扩展性上也有大量的挑战。在现代的机器上,处理器和内存的布局、数据在内存中的布局、多处理器体系结构中各个元素间的通信负载全都对性能有影响。此外,正确性和性能两者联系非常的紧密:算法的改进在提高性能的同时经常使其更加难以设计和检验正确的数据结构实现。

下面的例子阐述了影响数据结构设计的各个多处理器特征。假设我们想要实现一个共享的计数器数据结构,用于支持fetch-and-inc操作,即计数器加一然后返回增加前的值。一个普通的顺序fetch-and-inc实现的代码就如图1.1中左边部分所展示的那样。

如果我们允许多个线程并发地调用fetch-and-inc操作,上述实现运行起来并不正确。来看看原因,注意大多数编译器会把这份源代码转换成机器指令:把 X 的值装进一个寄存器,然后把寄存器中的值加一,然后再把这个寄存器的值存回 X 。假如计数器初始化为 0 ,两个不同的处理器并发的执行两个fetch-and-inc操作。然后就有可能两个操作都从 X 中读出 0 ,然后都把 1 存回 X 并且返回 0 。这显然是不正确的:两个操作中有一个应该返回 1 。

如上所述,两个fetch-and-inc操作不正确的交错结果导致了不正确的行为。一个自然并且普通的方法来阻止这样的交错就是用互斥锁(也被叫做 mutex 或者 lock)。锁是一个在任意时间点,都是不被(其他线程)获取,只被一个线程所获取的构造。如果一个线程 t1 希望获取已经被另一个线程 t2 所获取到的锁,那么 t1 必须等到 t2 释放这个锁。

如图 1.1 右半部分所示,我们能通过锁机制得到一个正确的顺序实现。我们通过阻止所有的交错来预防坏的交错。这样很容易得到一个正确的共享计数器,然而这种简单是有代价的:锁机制引发了许多关于性能和软件工程上的问题。


文章转自 并发编程网-ifeve.com

目录
相关文章
|
存储 安全 NoSQL
Java并发Map的面试指南:线程安全数据结构的奥秘
在计算机软件开发的世界里,多线程编程是一个重要且令人兴奋的领域。然而,与其引人入胜的潜力相伴而来的是复杂性和挑战,其中之一就是处理共享数据。当多个线程同时访问和修改共享数据时,很容易出现各种问题,如竞态条件和数据不一致性。
|
4月前
|
缓存 安全 Java
全面解读ConcurrentHashMap:Java中的高效并发数据结构
全面解读ConcurrentHashMap:Java中的高效并发数据结构
1529 2
|
5月前
|
存储 安全 C++
《C++ Concurrencyin Action》第6章--基于锁的并发数据结构设计
《C++ Concurrencyin Action》第6章--基于锁的并发数据结构设计
《C++ Concurrencyin Action》第6章--基于锁的并发数据结构设计
|
5月前
|
存储 安全 算法
《C++ Concurrencyin Action》第7章--无锁并发数据结构设计
《C++ Concurrencyin Action》第7章--无锁并发数据结构设计
|
存储 消息中间件 算法
多线程和并发编程(4)—并发数据结构之BlockingQueue
多线程和并发编程(4)—并发数据结构之BlockingQueue
81 0
|
存储 安全 Go
同样作为非并发安全的数据结构,slice和map在有并发安全问题时,为什么表现相差那么大
同样作为非并发安全的数据结构,slice和map在有并发安全问题时,为什么表现相差那么大
68 0
|
Java 应用服务中间件 C语言
打通 Java 任督二脉 —— 并发数据结构的基石
每一个 Java 的高级程序员在体验过多线程程序开发之后,都需要问自己一个问题,Java 内置的锁是如何实现的?最常用的最简单的锁要数 ReentrantLock,使用它加锁时如果没有立即加成功,就会阻塞当前的线程等待其它...
1126 0
|
12天前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
16 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
11天前
初步认识栈和队列
初步认识栈和队列
36 10
|
6天前
数据结构(栈与列队)
数据结构(栈与列队)
11 1