שירן חליבה concurrent queues. outline: some definitions 3 queue implementations : a bounded...

Download שירן חליבה Concurrent Queues. Outline: Some definitions 3 queue implementations : A Bounded Partial Queue An Unbounded Total Queue An Unbounded Lock-Free

Post on 24-Dec-2015

214 views

Category:

Documents

1 download

Embed Size (px)

TRANSCRIPT

  • Slide 1
  • Concurrent Queues
  • Slide 2
  • Outline: Some definitions 3 queue implementations : A Bounded Partial Queue An Unbounded Total Queue An Unbounded Lock-Free Queue
  • Slide 3
  • Introduction and some definitions: Pools show up in many places in concurrent systems. For example, in many applications, one or more producer threads produce items to be consumed by one or more consumer threads. To allow consumers to keep up, we can place a buffer between the producers and the consumers. Often, pools act as producerconsumer buffers. A pool allows the same item to appear more than once.
  • Slide 4
  • Introduction and some definitions cont. A queue is a special kind of pool with FIFO fairness. It provides an enq(x) method that puts item x at one end of the queue, called the tail, and a deq() method that removes and returns the item at the other end of the queue, called the head.
  • Slide 5
  • Bounded vs. Unbounded A pool can be bounded or unbounded. Bounded Fixed capacity Good when resources an issue Unbounded Holds any number of objects
  • Slide 6
  • Blocking vs. Non-Blocking Problem cases: Removing from empty pool Adding to full (bounded) pool Blocking Caller waits until state changes Non-Blocking Method throws exception
  • Slide 7
  • Total vs. Partial Pool methods may be total or partial. A method is total if calls do not wait for certain conditions to become true. For example, a get() call that tries to remove an item from an empty pool immediately returns a failure code or throws an exception. A total interface makes sense when the producer (or consumer) thread has something better to do than wait for the method call to take effect.
  • Slide 8
  • Total vs. Partial A method is partial if calls may wait for conditions to hold. For example, a partial get() call that tries to remove an item from an empty pool blocks until an item is available to return. A partial interface makes sense when the producer (or consumer) has nothing better to do than to wait for the pool to become nonempty (or non full).
  • Slide 9
  • Queue: Concurrency enq(x) y=deq() enq() and deq() work at different ends of the object tailhead
  • Slide 10
  • Concurrency enq(x) Challenge: what if the queue is empty or full? y=deq() tail head
  • Slide 11
  • A Bounded Partial Queue head tail deqLock enqLock Permission to enqueue 8 items permits 8 Lock out other enq() calls Lock out other deq() calls First actual item Sentinel
  • Slide 12
  • Enqueuer head tail deqLock enqLock permits 8 Lock enqLock Read permits OK No need to lock tail?
  • Slide 13
  • Enqueuer head tail deqLock enqLock permits 8 Enqueue Node 7 getAndDecrement() (Why atomic?)
  • Slide 14
  • Enqueuer head tail deqLock enqLock permits 8 Release lock 7 If queue was empty, notify waiting dequeuers
  • Slide 15
  • Unsuccesful Enqueuer head tail deqLock enqLock permits 0 Uh-oh Read permits
  • Slide 16
  • Dequeuer head tail deqLock enqLock permits 7 Lock deqLock Read sentinels next field OK
  • Slide 17
  • Dequeuer head tail deqLock enqLock permits 7 Read value
  • Slide 18
  • Dequeuer head tail deqLock enqLock permits 7 Make first Node new sentinel
  • Slide 19
  • Dequeuer head tail deqLock enqLock permits 7 Release deqLock
  • Slide 20
  • Dequeuer head tail deqLock enqLock permits 8 Increment permits (no need lock?) Answer: we had to hold the lock while enqueuing to prevent lots of enqueuers from proceeding without noticing that the capacity had been exceeded. Dequeuers will notice the queue is empty when they observe that the sentinels next field is null
  • Slide 21
  • Unsuccesful Dequeuer head tail deqLock enqLock permits 8 Read sentinels next field uh-oh
  • Slide 22
  • Bounded Queue public class BoundedQueue { ReentrantLock enqLock, deqLock; Condition notEmptyCondition, notFullCondition; AtomicInteger permits; Node head; Node tail; int capacity; enqLock = new ReentrantLock(); notFullCondition = enqLock.newCondition(); deqLock = new ReentrantLock(); notEmptyCondition = deqLock.newCondition(); }
  • Slide 23
  • The ReentrantLock is a monitor ( The mechanism that Java uses to support synchronization ). Allows blocking on a condition rather than spinning. How do we use it? (* More on monitors: http://www.artima.com/insidejvm/ed2/threadsynch.html )
  • Slide 24
  • Lock Conditions public interface Condition { void await(); boolean await(long time, TimeUnit unit); void signal(); void signalAll(); }
  • Slide 25
  • Await Releases lock associated with q Sleeps (gives up processor) Awakens (resumes running) Reacquires lock & returns q.await()
  • Slide 26
  • Signal Awakens one waiting thread Which will reacquire lock q.signal();
  • Slide 27
  • A Monitor Lock Critical Section waiting room Lock() unLock()
  • Slide 28
  • Unsuccessful Deq Critical Section waiting room Lock() await() Deq() Oh no, Empty!
  • Slide 29
  • Another One Critical Section waiting room Lock() await() Deq() Oh no, Empty!
  • Slide 30
  • Enqueur to the Rescue Critical Section waiting room Lock() signalAll() Enq( ) unLock() Yawn!
  • Slide 31
  • Monitor Signalling Critical Section waiting room Yawn! Awakend thread might still lose lock to outside contender
  • Slide 32
  • Dequeurs Signalled Critical Section waiting room Found it Yawn!
  • Slide 33
  • Dequeurs Signalled Critical Section waiting room Still empty!
  • Slide 34
  • Dollar Short + Day Late Critical Section waiting room
  • Slide 35
  • Why not signal()?
  • Slide 36
  • Lost Wake-Up Critical Section waiting room Lock() signal () Enq( ) unLock() Yawn!
  • Slide 37
  • Lost Wake-Up Critical Section waiting room Lock() Enq( ) unLock() Yawn!
  • Slide 38
  • Lost Wake-Up Critical Section waiting room Yawn!
  • Slide 39
  • Lost Wake-Up Critical Section waiting room Found it
  • Slide 40
  • Whats Wrong Here? Critical Section waiting room zzzz.!
  • Slide 41
  • Enq Method public void enq(T x) { boolean mustWakeDequeuers = false; enqLock.lock(); try { while (permits.get() == 0) notFullCondition.await(); Node e = new Node(x); tail.next = e; tail = e; if (permits.getAndDecrement() == capacity) mustWakeDequeuers = true; } finally { enqLock.unlock(); } }
  • Slide 42
  • Cont public void enq(T x) { if (mustWakeDequeuers) { deqLock.lock(); try { notEmptyCondition.signalAll(); } finally { deqLock.unlock(); }
  • Slide 43
  • The Enq() & Deq() Methods Share no locks Thats good But do share an atomic counter Accessed on every method call Thats not so good Can we alleviate this bottleneck? What is the problem?
  • Slide 44
  • Split the Counter The enq() method Decrements only Cares only if value is zero The deq() method Increments only Cares only if value is capacity
  • Slide 45
  • Split Counter Enqueuer decrements enqSidePermits Dequeuer increments deqSidePermits When enqueuer runs out Locks deqLock Transfers permits (dequeuer doesn't need permits- check head.next) Intermittent( ) synchronization Not with each method call Need both locks! (careful )
  • Slide 46
  • An Unbounded Total Queue Queue can hold an unbounded number of items. The enq() method always enqueues its item. The deq() throws EmptyException if there is no item to dequeue. No deadlock- each method acquires only one lock. Both the enq() and deq() methods are total as they do not wait for the queue to become empty or full.
  • Slide 47
  • An Unbounded Total Queue
  • Slide 48
  • A Lock-Free Queue Sentinel head tail Extension of the unbounded total queue Quicker threads help the slower threads Each nodes next field is an: AtomicReference The queue itself consists of two AtomicReference fields: head and tail
  • Slide 49
  • Compare and Set CAS
  • Slide 50
  • LockFreeQueue class
  • Slide 51
  • Enqueue head tail Enq( )
  • Slide 52
  • Enqueue head tail
  • Slide 53
  • Logical Enqueue head tail CAS
  • Slide 54
  • Physical Enqueue head tail Enqueue Node CAS
  • Slide 55
  • Enqueue These two steps are not atomic The tail field refers to either Actual last Node (good) Penultimate* Node (not so good) Be prepared! (*Penultimate :next to the last)
  • Slide 56
  • Enqueue What do you do if you find A trailing tail? Stop and fix it If tail node has non-null next field CAS the queues tail field to tail.next
  • Slide 57
  • When CASs Fail During logical enqueue Abandon hope, restart During physical enqueue Ignore it (why?)
  • Slide 58
  • LockFreeQueue class
  • Slide 59
  • Enq() Creat

Recommended

View more >