Транзакционная память – вторая часть
В прошлом посте мы рассмотрели концепцию транзакционной памяти – средство высокоуровневой организации работы параллельно выполняющихся задач. Рассмотрев основы операционной семантики, во многом совпадающие с семантикой транзакций, мы не упомянули несколько интересных расширений, предложенных в работе Composable Memory Transactions и воплощенных в языке Concurrent Haskell. О них и пойдет речь сегодня.
Оператор retry
Цель введения транзакционной памяти – предоставить высокоуровневую замену блокировкам, но согласование обращений к разделяемой памяти отнюдь не исчерпывает все использования блокировок. Еще один важный аспект – объекты синхронизации, которые используются для оповещения о событиях и блокирующего ожидания таких событий. Чтобы быть последовательной, методология транзакционной памяти должна предоставлять аналогичную высокоуровневую концепцию, которую можно использовать для ожидания событий.
Одним из первых предложений в этом направлении было введение условных критических областей (conditional critical regions, CCR) – аннотация транзакций некоторым условием, без правдивости которого транзакция не начинает выполнение. Этот подход работает, но не очень изящен, ибо требует создания новой транзакции даже тогда, когда транзакция уже запущена – то есть, только ради того, чтобы выразить семантику условного ожидания.
Современный взгляд на решение этой проблемы заключается в введении оператора retry, который сигнализирует о необходимости перезапуска транзакции – комбинация этого оператора с условным оператором предоставляет конструкт ожидания некоторого события и во многих случаях делает явную сигнализацию события избыточной. Ниже приведено сравнение реализаций блокирующей коллекции ограниченной емкости, аналогичной коллекции BlockingCollection<T> из .NET Framework 4.0 (реализация, основанная на примитивах синхронизации намеренно упрощена в целях краткости).
Блокирующая коллекция
(реализована при помощи примитивов синхронизации)
Блокирующая коллекция
(реализована при помощи оператора retry)
public class BlockingCollection<T>
{
private Object _lock = new Object();
public void Put(T item)
{
while (Monitor.Wait(_lock, Timeout.Inifinite))
{
try