Очередь Майкла и Скотта — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Структура очереди)
Строка 5: Строка 5:
 
Очередь моделируется с помощью односвязного списка. Каждый элемент списка (<tex>Node</tex>) содержит ссылку на хранимые в нём данные и указатель на следующий элемент списка (который можно менять атомарно).
 
Очередь моделируется с помощью односвязного списка. Каждый элемент списка (<tex>Node</tex>) содержит ссылку на хранимые в нём данные и указатель на следующий элемент списка (который можно менять атомарно).
  
  '''case class''' Node<T>('''val''' data: T, '''val''' next: AtomicReference<Node<T>>)
+
  '''case class''' Node<'''T'''>('''val''' data: '''T''', '''val''' next: AtomicReference<Node<'''T'''>>)
  
 
Если узел <tex>node</tex> является последним в списке, то его <tex>next</tex> указывает на <tex>null</tex>.
 
Если узел <tex>node</tex> является последним в списке, то его <tex>next</tex> указывает на <tex>null</tex>.
Строка 13: Строка 13:
 
Узел списка, на который указывает <tex>H</tex>, является фиктивным (''dummy''). Данные, хранимые в этом узле, не имеют значения. Изначально очередь состоит из одного ''dummy''-элемента, на который указывают <tex>T</tex> и <tex>H</tex>.
 
Узел списка, на который указывает <tex>H</tex>, является фиктивным (''dummy''). Данные, хранимые в этом узле, не имеют значения. Изначально очередь состоит из одного ''dummy''-элемента, на который указывают <tex>T</tex> и <tex>H</tex>.
  
  '''class''' Queue<T>
+
  '''class''' Queue<'''T'''>
     dummy = new Node(null, new AtomicReference<Node>(null))
+
     dummy = new Node<'''T'''>(null, new AtomicReference<Node>(null))
     head = new AtomicReference<Node>(dummy)
+
     head = new AtomicReference<Node<'''T'''>>(dummy)
     tail = new AtomicReference<Node>(dummy)
+
     tail = new AtomicReference<Node<'''T'''>>(dummy)
  
 
// TODO; картинка
 
// TODO; картинка
  
 
Будем поддерживать следующий инвариант: в нашей очереди <tex>H</tex> указывает на узел, находящийся не правее узла, на который указывает <tex>T</tex>
 
Будем поддерживать следующий инвариант: в нашей очереди <tex>H</tex> указывает на узел, находящийся не правее узла, на который указывает <tex>T</tex>
 +
 +
==Идея реализации==
 +
 +
=== Удаление элемента ===
 +
 +
Для удаления элемента необходимо переместить указатель <tex>H</tex> на следующую в списке вершину.
 +
 +
'''def''' pop(): '''T'''
 +
    if (H.next == null):
 +
        '''throw''' new EmptyException()
 +
    H = H.next
 +
    return H.data <font color=green>H - новый фиктивный элемент</font>
 +
 +
=== Добавление элемента ===
  
 
==Примечания==
 
==Примечания==

Версия 17:38, 1 октября 2018

Эта статья находится в разработке!

Очередь Майкла и Скотта (Michael-Scott Queue) - алгоритм построения lock-free очереди. Впервые был предложен Maged M. Michael и Michael L. Scot в статье [1].

Структура очереди

Очередь моделируется с помощью односвязного списка. Каждый элемент списка ([math]Node[/math]) содержит ссылку на хранимые в нём данные и указатель на следующий элемент списка (который можно менять атомарно).

case class Node<T>(val data: T, val next: AtomicReference<Node<T>>)

Если узел [math]node[/math] является последним в списке, то его [math]next[/math] указывает на [math]null[/math].

Сама очередь состоит из двух указателей: на голову [math]H[/math] и на хвост [math]T[/math], которые можно менять атомарно. Удаление из очереди происходит со стороны головы, добавление - со стороны хвоста.

Узел списка, на который указывает [math]H[/math], является фиктивным (dummy). Данные, хранимые в этом узле, не имеют значения. Изначально очередь состоит из одного dummy-элемента, на который указывают [math]T[/math] и [math]H[/math].

class Queue<T>
    dummy = new Node<T>(null, new AtomicReference<Node>(null))
    head = new AtomicReference<Node<T>>(dummy)
    tail = new AtomicReference<Node<T>>(dummy)

// TODO; картинка

Будем поддерживать следующий инвариант: в нашей очереди [math]H[/math] указывает на узел, находящийся не правее узла, на который указывает [math]T[/math]

Идея реализации

Удаление элемента

Для удаления элемента необходимо переместить указатель [math]H[/math] на следующую в списке вершину.

def pop(): T
    if (H.next == null):
        throw new EmptyException()
    H = H.next
    return H.data H - новый фиктивный элемент

Добавление элемента

Примечания

Источники информации

  • Maurice Herliny & Nir Shavit - The Art of Multiprocessor programming, стр 230