<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.157.206&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.157.206&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/217.66.157.206"/>
		<updated>2026-04-29T15:12:02Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_%D0%9C%D0%B0%D0%B9%D0%BA%D0%BB%D0%B0_%D0%B8_%D0%A1%D0%BA%D0%BE%D1%82%D1%82%D0%B0&amp;diff=66393</id>
		<title>Очередь Майкла и Скотта</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_%D0%9C%D0%B0%D0%B9%D0%BA%D0%BB%D0%B0_%D0%B8_%D0%A1%D0%BA%D0%BE%D1%82%D1%82%D0%B0&amp;diff=66393"/>
				<updated>2018-10-03T21:04:41Z</updated>
		
		<summary type="html">&lt;p&gt;217.66.157.206: /* Структура очереди */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Очередь Майкла и Скотта''' ''(Michael-Scott Queue)'' - алгоритм построения lock-free очереди. Впервые был предложен Maged M. Michael и  Michael L. Scot в статье &amp;lt;ref&amp;gt;[http://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf? Simple, Fast, and Practical Non-Blocking and BlockingConcurrent Queue Algorithms]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
== Структура очереди ==&lt;br /&gt;
&lt;br /&gt;
Очередь моделируется с помощью односвязного списка. Каждый элемент списка (&amp;lt;tex&amp;gt;Node&amp;lt;/tex&amp;gt;) содержит ссылку на хранимые в нём данные и указатель на следующий элемент списка (который можно менять атомарно).&lt;br /&gt;
&lt;br /&gt;
 '''case class''' Node('''val''' data: '''Int''', '''val''' next: AtomicReference&amp;lt;Node&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Если узел &amp;lt;tex&amp;gt;node&amp;lt;/tex&amp;gt; является последним в списке, то его &amp;lt;tex&amp;gt;next&amp;lt;/tex&amp;gt; указывает на &amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сама очередь состоит из двух указателей: на голову &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; и на хвост &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;, которые можно менять атомарно. Удаление из очереди происходит со стороны головы, добавление - со стороны хвоста.&lt;br /&gt;
&lt;br /&gt;
Узел списка, на который указывает &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt;, является фиктивным (''dummy''). Данные, хранимые в этом узле, не имеют значения. Изначально очередь состоит из одного ''dummy''-элемента, на который указывают &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 '''class''' Queue&lt;br /&gt;
     dummy = '''new''' Node(null, '''new''' AtomicReference&amp;lt;Node&amp;gt;(null))&lt;br /&gt;
     head = '''new''' AtomicReference&amp;lt;Node&amp;gt;(dummy)&lt;br /&gt;
     tail = '''new''' AtomicReference&amp;lt;Node&amp;gt;(dummy)&lt;br /&gt;
&lt;br /&gt;
Будем поддерживать следующий инвариант: в нашей очереди &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; указывает на узел, находящийся не правее узла, на который указывает &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Идея реализации==&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента ===&lt;br /&gt;
&lt;br /&gt;
Для удаления элемента необходимо переместить указатель &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; на следующую в списке вершину.&lt;br /&gt;
&lt;br /&gt;
 '''def''' pop(): '''Int'''&lt;br /&gt;
     if (H.next == null):&lt;br /&gt;
         '''throw''' '''new''' EmptyException()&lt;br /&gt;
     H = H.next&lt;br /&gt;
     '''return''' H.data &amp;lt;font color=green&amp;gt;//H - новый фиктивный элемент&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Добавление элемента ===&lt;br /&gt;
&lt;br /&gt;
Создадим новый узел списка, и добавим его в конец очереди. &lt;br /&gt;
&lt;br /&gt;
 '''def''' push(x: '''Int'''):&lt;br /&gt;
     newTail = '''new''' Node(x, new AtomicReference&amp;lt;Node&amp;gt;(null))&lt;br /&gt;
     T.next = newTail &amp;lt;font color=green&amp;gt;//Добавление новой вершины в очередь&amp;lt;/font&amp;gt;&lt;br /&gt;
     T = T.next &amp;lt;font color=green&amp;gt;//Изменение хвоста списка&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Многопоточная реализация ==&lt;br /&gt;
&lt;br /&gt;
Будем при всех изменениях указателей на вершины списка использовать &amp;lt;tex&amp;gt;CAS&amp;lt;/tex&amp;gt; (то есть при изменении  &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt;, и &amp;lt;tex&amp;gt;T.next&amp;lt;/tex&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== Удаление элемента ===&lt;br /&gt;
&lt;br /&gt;
Для удаления элемента необходимо переместить указатель &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; на следующую в списке вершину.&lt;br /&gt;
&lt;br /&gt;
 '''def''' pop(): '''Int'''&lt;br /&gt;
     '''while''' ('''true'''): &amp;lt;font color=green&amp;gt;//Поток пытается в CAS - цикле поменять указатель на H, пока не получится&amp;lt;/font&amp;gt;&lt;br /&gt;
         head = H.get()&lt;br /&gt;
         if (head.next == null):&lt;br /&gt;
             '''throw''' '''new''' EmptyException()&lt;br /&gt;
         newHead = head.next.get()&lt;br /&gt;
         if ('''CAS'''(H, head, nextHead)):&lt;br /&gt;
             '''return''' newHead.data&lt;br /&gt;
&lt;br /&gt;
=== Добавление элемента ===&lt;br /&gt;
&lt;br /&gt;
Создадим новый узел списка, и добавим его в конец очереди. &lt;br /&gt;
&lt;br /&gt;
 '''def''' push(x: '''Int'''):&lt;br /&gt;
     newTail = '''new''' Node(x, '''new''' AtomicReference&amp;lt;Node&amp;gt;(null))&lt;br /&gt;
     '''while''' ('''true'''): &amp;lt;font color=green&amp;gt;//Поток пытается в CAS - цикле поменять T.next, пока не получится&amp;lt;/font&amp;gt;&lt;br /&gt;
         tail = T.get()&lt;br /&gt;
         curTail = tail.next&lt;br /&gt;
         if ('''CAS'''(curTail, null, newTail)): &amp;lt;font color=green&amp;gt;//Поток пытается добавить элемент в конец очереди&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''break'''&lt;br /&gt;
    '''while''' ('''true'''): &amp;lt;font color=green&amp;gt;//Поток пытается в CAS - цикле поменять указатель на T, пока не получится&amp;lt;/font&amp;gt;&lt;br /&gt;
         tail = T.get()&lt;br /&gt;
         nextTail = tail.next.get()&lt;br /&gt;
         if ('''CAS'''(T, tail, nextTail)):&lt;br /&gt;
             '''break'''&lt;br /&gt;
&lt;br /&gt;
При данной реализации мы сталкиваемся со следующей проблемой&lt;br /&gt;
&lt;br /&gt;
== Описание проблемы ==&lt;br /&gt;
&lt;br /&gt;
Рассмотрим ситуацию, при которой два потока &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; добавляют в очередь элементы &amp;lt;tex&amp;gt;elem&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;elem'&amp;lt;/tex&amp;gt;. Рассмотрим следующую последовательность действий:&lt;br /&gt;
&lt;br /&gt;
# Поток &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; добавляет в очередь новую вершину, изменяя &amp;lt;tex&amp;gt;T.next&amp;lt;/tex&amp;gt;, но не успевает изменить &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; так, чтобы он указывал на только что добавленную вершину.&lt;br /&gt;
# Планировщик операционной системы усыпляет поток &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Поток &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; собирается добавить новую вершину в очередь, но не может этого сделать, так как постоянно проваливает операцию &amp;lt;tex&amp;gt;CAS(T.next, null, newTail)&amp;lt;/tex&amp;gt; (T.next не указывает на &amp;lt;tex&amp;gt;null&amp;lt;/tex&amp;gt;, так как поток &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; на шаге &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; добавил в очередь новую вершину, но не передвинул &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;)&lt;br /&gt;
# Поток &amp;lt;tex&amp;gt;B&amp;lt;/tex&amp;gt; не сможет добавить в очередь новую вершину (а следовательно, завершить операцию &amp;lt;tex&amp;gt;push&amp;lt;/tex&amp;gt;), до тех пор, пока планировщик операционной системы не разбудит поток &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, и поток &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; не завершит добавление (то есть не передвинет &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; на вершину, добавленную на шаге &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.)&lt;br /&gt;
&lt;br /&gt;
Следовательно, у такой очереди нет гарантии прогресса, и этот алгоритм не lock-free.&lt;br /&gt;
&lt;br /&gt;
== Корректная lock-free реализация ==&lt;br /&gt;
&lt;br /&gt;
=== Основная идея ===&lt;br /&gt;
&lt;br /&gt;
Нельзя выполнить добавление элемента в очередь и перемещение &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; атомарно. В таком случае, пусть остальные потоки помогают перенести указатель на хвост очереди. Если поток видит непустой &amp;lt;tex&amp;gt;T.next&amp;lt;/tex&amp;gt; (то есть если он провалил &amp;lt;tex&amp;gt;CAS(tail.next, null, newTail)&amp;lt;/tex&amp;gt;), то он должен помочь перенести &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;, то есть выполнить &amp;lt;tex&amp;gt;CAS(T, tail, tail.next.get())&amp;lt;/tex&amp;gt; однократно. Если &amp;lt;tex&amp;gt;CAS&amp;lt;/tex&amp;gt; выполнен успешно, то хвост перемещён успешно (а значит, наш поток должен вернуться к добавлению нового элемента). Если же он выполнен неудачно, то это значит, что &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; уже не указывает на &amp;lt;tex&amp;gt;tail&amp;lt;/tex&amp;gt;, а значит, другой поток уже успешно переместил хвост (а значит, наш поток должен вернуться к добавлению нового элемента).&lt;br /&gt;
&lt;br /&gt;
=== Реализация &amp;lt;tex&amp;gt;push&amp;lt;/tex&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
 '''def''' push(x: '''Int'''):&lt;br /&gt;
     newTail = '''new''' Node(x, '''new''' AtomicReference&amp;lt;Node&amp;gt;(null))&lt;br /&gt;
     '''while''' ('''true'''): &amp;lt;font color=green&amp;gt;//CAS-цикл&amp;lt;/font&amp;gt;&lt;br /&gt;
         tail = T.get()&lt;br /&gt;
         '''if''' ('''CAS'''(tail.next, '''null''', newTail)):&lt;br /&gt;
             &amp;lt;font color=green&amp;gt;/*&lt;br /&gt;
             Если T указывает на последний добавленный элемент и &lt;br /&gt;
             получилось добавить ещё один элемент в хвост, &lt;br /&gt;
             пробуем передвинуть T. Если не получилось передвинуть T,&lt;br /&gt;
             значит, другой поток сделал это за нас, завершаем работу.&lt;br /&gt;
             Если получилось - то мы сами передвинули T, завершаем работу&lt;br /&gt;
             */&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''CAS'''(T, tail, newTail)&lt;br /&gt;
             '''return'''&lt;br /&gt;
         else:&lt;br /&gt;
             &amp;lt;font color=green&amp;gt;/*&lt;br /&gt;
             Если T - не последний добавленный элемент элемент, то передвигаем T на последний элемент&lt;br /&gt;
             Если этого сделать не получилось, значит, это сделал другой поток.&lt;br /&gt;
             Если получилось - значит, наш поток передвинул T на текущий последний элемент.&lt;br /&gt;
             В любом случае, возвращаемся в начало CAS-цикла, чтобы завершить добавление в очередь новой вершины.&lt;br /&gt;
             */&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''CAS'''(T, tail, tail.next.get())&lt;br /&gt;
&lt;br /&gt;
== Проблема с &amp;lt;tex&amp;gt;pop&amp;lt;/tex&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
Если мы попытаемся воспользоваться написанной выше реализацией метода &amp;lt;tex&amp;gt;pop&amp;lt;/tex&amp;gt;, инвариант очереди не будет соблюдён. В силу особенностей реализации метода &amp;lt;tex&amp;gt;push&amp;lt;/tex&amp;gt;, в некоторые моменты &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; может указывать не на добавленный последним элемент, а на добавленный предпоследним. В таком случае, с помощью последовательности удалений можно добиться того, что &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; будет указывать на последний добавленный элемент, а &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; - на предпоследний. Таким образом, &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt; будет указывать на вершину правее чем та, на которую указывает &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;, то есть инвариант очереди будет нарушен&lt;br /&gt;
&lt;br /&gt;
== Корректная реализация &amp;lt;tex&amp;gt;pop&amp;lt;/tex&amp;gt; ==&lt;br /&gt;
&lt;br /&gt;
Основная проблема предыдущей реализации состоит в том, что в методе &amp;lt;tex&amp;gt;pop&amp;lt;/tex&amp;gt; при перемещении &amp;lt;tex&amp;gt;H&amp;lt;/tex&amp;gt;, мы никак не следили за положением &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Эту проблему можно исправить следующим образом: пусть в методе &amp;lt;tex&amp;gt;pop&amp;lt;/tex&amp;gt; рабочий поток будет помогать переместить указатель &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; на последний добавленный элемент (аналогично действиям рабочего потока в методе &amp;lt;tex&amp;gt;push&amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Для определения того, указывает ли &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; на последний добавленный элемент, воспользуемся следующим соображением: если &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; указывает на последний добавленный элемент, то &amp;lt;tex&amp;gt;T.get().next == '''null'''&amp;lt;/tex&amp;gt;, так как за последним добавленным элементом нет других элементов. В противном случае &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; указывает на предпоследний добавленный элемент, и его надо передвинуть на последний добавленный.&lt;br /&gt;
&lt;br /&gt;
 '''def''' pop(): '''Int'''&lt;br /&gt;
     newTail = '''new''' Node(x, '''new''' AtomicReference&amp;lt;Node&amp;gt;(null))&lt;br /&gt;
     '''while''' ('''true'''): &amp;lt;font color=green&amp;gt;//CAS-цикл&amp;lt;/font&amp;gt;&lt;br /&gt;
         head = H.get() &amp;lt;font color=green&amp;gt;//Сохраняем в локальные переменные текущие голову и хвост, а так же следующий за головным элемент&amp;lt;/font&amp;gt;&lt;br /&gt;
         tail = T.get()&lt;br /&gt;
         nextHead = head.next.get()&lt;br /&gt;
         '''if''' (head == tail):&lt;br /&gt;
             &amp;lt;font color=green&amp;gt;/*&lt;br /&gt;
             Если head и tail совпадают, это ещё не означает, что очередь пуста.&lt;br /&gt;
             Возможно, что мы просто не успели подвинуть tail. Если tail.next не null,&lt;br /&gt;
             то мы просто не успели подвинуть tail при добавлении.&lt;br /&gt;
             */&amp;lt;/font&amp;gt;&lt;br /&gt;
             '''if''' (nextHead == '''null'''):&lt;br /&gt;
                 &amp;lt;font color=green&amp;gt;// Следующего элемента нет, очередь пуста&amp;lt;/font&amp;gt;&lt;br /&gt;
                 '''throw''' '''new''' EmptyException()&lt;br /&gt;
             '''else''':&lt;br /&gt;
                 &amp;lt;font color=green&amp;gt;/*&lt;br /&gt;
                 push не успел подвинуть T, наш поток должен помочь&lt;br /&gt;
                 tail == head =&amp;gt; tail.next == head.next&lt;br /&gt;
                 */&amp;lt;/font&amp;gt;&lt;br /&gt;
        '''else''':&lt;br /&gt;
            &amp;lt;font color=green&amp;gt;// Очередь гарантированно не пуста, следующий элемент существует&amp;lt;/font&amp;gt;&lt;br /&gt;
            result = nextHead.data&lt;br /&gt;
            '''if''' ('''CAS'''(H, head, nextHead)):&lt;br /&gt;
                &amp;lt;font color=green&amp;gt;/*&lt;br /&gt;
                Если получилось переставить голову, то фиктивным элементом стал&lt;br /&gt;
                H.next, результат - данные, которые в нём лежали. Если не получилось - &lt;br /&gt;
                возвращаемся в начало метода и пробуем ещё раз&lt;br /&gt;
                */&amp;lt;/font&amp;gt;&lt;br /&gt;
                '''return''' result&lt;br /&gt;
&lt;br /&gt;
==Примечания==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* Maurice Herliny &amp;amp; Nir Shavit - The Art of Multiprocessor programming, стр 230&lt;br /&gt;
[[Категория: Параллельное программирование]]&lt;/div&gt;</summary>
		<author><name>217.66.157.206</name></author>	</entry>

	</feed>