Non-blocking algorithm(非阻塞算法,非阻塞同步的算法实现)

Non-blocking algorithm

In computer science, a non-blocking algorithm ensures that threads competing
for a shared resource do not have their execution indefinitely
postponed by mutual exclusion. A non-blockingalgorithm is lock-free if
there is guaranteed system-wide progress regardless of schedulingwait-free if
there is also guaranteed per-thread progress.

Literature up to the turn of the 21st century used "non-blocking" synonymously with lock-free. However, since 2003,[1] the
term has been weakened to only prevent progress-blocking interactions with a preemptive scheduler.
In modern usage, therefore, an algorithm is non-blocking if the suspension of one or more threads will not stop the potential progress of the remaining threads. They are designed to avoid requiring a critical
section
. Often, these algorithms allow multiple processes to make progress on a problem without ever blocking each other. For some operations, these algorithms provide an alternative to locking
mechanisms
.

Motivation[edit]

Main article: Disadvantages of locks

The traditional approach to multi-threaded programming is to use locks to synchronize access
to shared resources. Synchronization primitives such as mutexessemaphores,
and critical sections are all mechanisms by which a programmer can ensure that certain sections of code do
not execute concurrently, if doing so would corrupt shared memory structures. If one thread attempts to acquire a lock that is already held by another thread, the thread will block until the lock is free.

Blocking a thread is undesirable for many reasons. An obvious reason is that while the thread is blocked, it cannot accomplish anything. If the blocked thread was performing a high-priority orreal-time task,
it would be highly undesirable to halt its progress. Other problems are less obvious. Certain interactions between locks can lead to error conditions such as deadlocklivelock,
and priority inversion. Using locks also involves a trade-off between coarse-grained locking, which can
significantly reduce opportunities for parallelism, and fine-grained locking, which requires more careful
design, increases locking overhead and is more prone to bugs.

Non-blocking algorithms are also safe for use in interrupt handlers: even though the preempted thread
cannot be resumed, progress is still possible without it. In contrast, global data structures protected by mutual exclusion cannot safely be accessed in a handler, as the preempted thread may be the one holding the lock.

Implementation[edit]

With few exceptions, non-blocking algorithms use atomic read-modify-write primitives
that the hardware must provide, the most notable of which is compare and swap (CAS)Critical
sections
are almost always implemented using standard interfaces over these primitives. Until recently, all non-blocking algorithms had to be written "natively" with the underlying primitives to achieve acceptable performance. However, the emerging field
of software transactional memory promises standard abstractions for writing efficient
non-blocking code. [2][3]

Much research has also been done in providing basic data structures such as stacksqueuessets,
and hash tables. These allow programs to easily exchange data between threads asynchronously.

Additionally, some non-blocking data structures are weak enough to be implemented without special atomic primitives. These exceptions include:

  • single-reader single-writer ring buffer FIFO
  • Read-copy-update with a single writer and any number of readers. (The readers
    are wait-free; the writer is usually lock-free, until it needs to reclaim memory).
  • Read-copy-update with multiple writers and any number of readers. (The readers
    are wait-free; multiple writers generally serialize with a lock and are not obstruction-free).

Several libraries internally use lock-free techniques,[4][5] but
it is difficult to write lock-free code that is correct.[6][7][8][9]

Wait-freedom[edit]

Wait-freedom is the strongest non-blocking guarantee of progress, combining guaranteed system-wide throughput with starvation-freedom.
An algorithm is wait-free if every operation has a bound on the number of steps the algorithm will take before the operation completes. This property is critical for real-time systems and is always nice to have as long as the performance cost is not too high.

It was shown in the 1980s[10] that
all algorithms can be implemented wait-free, and many transformations from serial code, called universal constructions, have been demonstrated. However, the resulting performance does not in general match even na?ve blocking designs. Several papers
have since improved the performance of universal constructions, but still, their performance is far below blocking designs.

Several papers have investigated the difficulty of creating wait-free algorithms. For example, it has been shown[11] that
the widely available atomic conditional primitives, CAS and LL/SC,
cannot provide starvation-free implementations of many common data structures without memory costs growing linearly in the number of threads.

But in practice these lower bounds do not present a real barrier as spending a cache line or exclusive reservation granule (up to 2kb on ARM) of store per thread in the shared memory is not considered too costly for practical systems (typically the amount of
store logically required is a word, but physically CAS operations on the same cache line will collide, and LL/SC operations in the same exclusive reservation granule will collide, so the amount of store physically required[citation
needed
]
 is greater).

Until 2011, wait-free algorithms were rare, both in research and in practice. However, in 2011 Kogan and Petrank[12] presented
a wait-free queue building on the CAS primitive, generally available on common hardware. Their construction
expands the lock-free queue of Michael and Scott,[13] which
is an efficient queue often used in practice. A follow-up paper by Kogan and Petrank[14] provided
a methodology for making wait-free algorithms fast and used this methodology to make the wait-free queue practically as fast as its lock-free counterpart.

Lock-freedom[edit]

Lock-freedom allows individual threads to starve but guarantees system-wide throughput. An algorithm is lock-free if it satisfies that when the program threads are run sufficiently long at least one of the threads makes progress (for some sensible definition
of progress). All wait-free algorithms are lock-free.

In general, a lock-free algorithm can run in four phases: completing one‘s own operation, assisting an obstructing operation, aborting an obstructing operation, and waiting. Completing one‘s own operation is complicated by the possibility of concurrent assistance
and abortion, but is invariably the fastest path to completion.

The decision about when to assist, abort or wait when an obstruction is met is the responsibility of a contention manager. This may be very simple (assist higher priority operations, abort lower priority ones), or may be more optimized to achieve better
throughput, or lower the latency of prioritized operations.

Correct concurrent assistance is typically the most complex part of a lock-free algorithm, and often very costly to execute: not only does the assisting thread slow down, but thanks to the mechanics of shared memory, the thread being assisted will be slowed,
too, if it is still running.

Obstruction-freedom[edit]

Obstruction-freedom is possibly the weakest natural non-blocking progress guarantee. An algorithm is obstruction-free if at any point, a single thread executed in isolation (i.e., with all obstructing threads suspended) for a bounded number of steps will complete
its operation. All lock-free algorithms are obstruction-free.

Obstruction-freedom demands only that any partially completed operation can be aborted and the changes made rolled back. Dropping concurrent assistance can often result in much simpler algorithms that are easier to validate. Preventing the system from continually live-locking is
the task of a contention manager.

Obstruction-freedom is also called optimistic concurrency control.

Some obstruction-free algorithms use a pair of "consistency markers" in the data structure. Processes reading the data structure first read one consistency marker, then read the relevant data into an internal buffer, then read the other marker, and then compare
the markers. The data is consistent if the two markers are identical. Markers may be non-identical when the read is interrupted by another process updating the data structure. In such a case, the process discards the data in the internal buffer and tries again.

时间: 2024-10-12 16:41:14

Non-blocking algorithm(非阻塞算法,非阻塞同步的算法实现)的相关文章

socket阻塞与非阻塞,同步与异步、I/O模型,select与poll、epoll比较

1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步:      所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:      异步的概念和同步相对.当c端一个异步过程调用发出后,调

IO-同步,异步,阻塞,非阻塞

IO-同步,异步,阻塞,非阻塞1.什么是IO数据在系统内核(kernel)和用户进程之间的传递,称为IO. 2.IO操作步骤以read为例,涉及两个系统对象,调用IO的process(or thread),即用户进程:另一个为系统内核(kernel).当用户进程调用recvfrom操作时,会经历两个阶段1)等待数据准备2)将数据从内核拷贝至进程中 3.IO模型根据用户进程在IO操作时的状态,可以分为5中IO类型:blocking IO:阻塞IOnon-blocking IO:非阻塞IOIO mu

socket阻塞与非阻塞,同步与异步、I/O模型

socket阻塞与非阻塞,同步与异步 作者:huangguisu 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:同步:      所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:      异步的概念和同步相对

[Z] IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)

原文链接:http://blog.csdn.net/historyasamirror/article/details/5778378 当你发现自己最受欢迎的一篇blog其实大错特错时,这绝对不是一件让人愉悦的事.<IO - 同步,异步,阻塞,非阻塞 >是我在开始学习epoll和libevent的时候写的,主要的思路来自于文中的那篇link .写完之后发现很多人都很喜欢,我还是非常开心的,也说明这个问题确实困扰了很多人.随着学习的深入,渐渐的感觉原来的理解有些偏差,但是还是没引起自己的重视,觉着

IO中同步异步,阻塞与非阻塞 -- 原理篇

再补一篇高手写的理论分析,便于更深刻理解 转自:http://blog.csdn.net/historyasamirror/article/details/5778378 ============================================================= 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答

聊聊阻塞与非阻塞、同步与异步、I/O模型

1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端:  同步: 所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步: 异步的概念和同步相对.当c端一个异步过程调用发出后,调用者不能立刻得到结

【转载】socket阻塞与非阻塞,同步与异步、I/O模型

转自:http://blog.csdn.net/hguisu/article/details/7453390 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:同步:      所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任

socket编程的同步、异步与阻塞、非阻塞示例详解

socket编程的同步.异步与阻塞.非阻塞示例详解之一 分类: 架构设计与优化 简介图 1. 基本 Linux I/O 模型的简单矩阵 每个 I/O 模型都有自己的使用模式,它们对于特定的应用程序都有自己的优点.本节将简要对其一一进行介绍. 一.同步阻塞模式在这个模式中,用户空间的应用程序执行一个系统调用,并阻塞,直到系统调用完成为止(数据传输完成或发生错误). /* * \brief * tcp client */ #include <stdio.h> #include <stdlib

Socket编程中,阻塞与非阻塞的区别

阻塞:一般的I/O操作可以在新建的流中运用.在服务器回应前它等待客户端发送一个空白的行.当会话结束时,服务器关闭流和客户端socket.如果在队列中没有请示将会出现什么情况呢?那个方法将会等待一个的到来.这个行为叫阻塞.accept()方法将会阻塞服务器线程直到一个呼叫到来.当5个连接处理完闭之后,服务器退出.任何的在队列中的呼叫将会被取消. 非阻塞:非阻塞套接字是指执行此套接字的网络调用时,不管是否执行成功,都立即返回.比如调用recv()函数读取网络缓冲区中数据,不管是否读到数据都立即返回,

java 多线程阻塞队列 与 阻塞方法与和非阻塞方法

Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移除元素的.在FIFO队列中,所有新元素都插入队列的末尾.队列都是线程安全的,内部已经实现安全措施,不用我们担心 Queue中的方法 Queue中的方法不难理解,6个,每2对是一个也就是总共3对.看一下JDK API就知道了: 注意一点就好,Queue通常不允许插入Null,尽管某些实现(比如Link