2015-05-11 3 views
-2

Я выполнил простую задачу по созданию списка фиксированного размера, который позволяет выполнять одновременную запись и может в любой момент удалять последний снимок элементов в списке.Конкретный список с фиксированным размером

Вот моя реализация. Смещение будет увеличиваться атомарно для каждого потока и сбросить, если достигнет размера списка. Различные потоки должны иметь изолированный доступ к каждой части массива.

Мой вопрос, когда я вызываю Dump(), первые несколько элементов не хранятся в списке. Кроме того, есть ли функция блокировки, которая может выполнять как увеличение, так и сброс, так что мне не нужно создавать объект-блокиратор и блокировку? Благодарю.

public static void Main(string[] args) 
{    
    ConcurrentCircularFixedList<int> list = new ConcurrentCircularFixedList<int>(20); 
    Enumerable.Range(1, 30).AsParallel().Select(nu => list.Enqueu(nu)).ToList();  
} 

public class ConcurrentCircularFixedList<T> 
{ 
    private int _size; 
    private int _offset; 
    private sealed object _locker = new Object(); 
    privateT[] _list; 

    public ConcurrentCircularFixedList(int size) 
    { 
     _size = size; 
     _offset = 0; 
     _list = new T[_size]; 
    } 

    public int Enqueu(T item) 
    { 
     _list[_offset] = item; 
     lock(_locker) 
     { 
      Debug.Write("B " + _offset); 
      _offset += 1; 
      if(_offset == _size) 
       _offset = 0; 
      Debug.Write("A " + _offset + "\n"); 
     } 
     return _offset; 
    } 

    public T[] Dump() 
    { 
     return _list.ToArray(); 
    } 
} 
+0

Нет блокировки на 'Dump', однако я не считаю, что блокировка необходима. – Aron

+0

Я голосую, чтобы закрыть этот вопрос как не по теме, потому что это принадлежит CR. – Aron

+0

@BrianRasmussen, вы, ребята, быстро, я случайно нажал кнопку отправки. Пожалуйста, см. Мои вопросы. – Helic

ответ

2

Вот небольшая версия списка без блокировки, который копирует при записи. Перед использованием следует четко понимать характеристики производительности. Это дорого, когда у вас много авторов или список большой. Считывание является синхронизацией, так как список является фактически неизменным. Конечно, это можно было бы улучшить разными способами, но вы получите эту идею. По сути, он жертвует некоторым давлением памяти и медленнее пишет для чтения с нулевой стоимостью.

public class CopyWriteList<T> 
{ 
    private volatile List<T> list; 

    public CopyWriteList() 
    { 
     list = new List<T>(); 
    } 

    public CopyWriteList(int capacity) 
    { 
     list = new List<T>(capacity); 
    } 

    public T this[int index] 
    { 
     get { return list[index]; } 
     set { Replace(x => x[index] = value); } 
    } 

    public void Clear() 
    { 
     Replace(x => x.Clear()); 
    } 

    public void Add(T item) 
    { 
     Replace(x => x.Add(item)); 
    } 

    //Etc.... 

    private void Replace(Action<List<T>> action) 
    { 
     List<T> current; 
     List<T> updated; 
     do 
     { 
      current = list; 
      updated = new List<T>(current); 
      action(updated); 
     } while (Interlocked.CompareExchange(ref list, updated, current) != current); 
    } 

    public List<T> GetSnapshot() 
    { 
     return list; 
    } 
} 

Альтернативно, это фиксированная версия вашего кода. Обратите внимание, что между обоими читателями и писателями есть разногласия. Из-за этого производительность может пострадать (например, дорогое переключение контекста).

public class ConcurrentCircularFixedList<T> 
{ 
    private readonly int _size; 
    private int _offset; 
    private readonly object _locker = new Object(); 
    private readonly T[] _list; 

    public ConcurrentCircularFixedList(int size) 
    { 
     _size = size; 
     _offset = 0; 
     _list = new T[_size]; 
    } 

    public int Enqueue(T item) 
    { 
     lock (_locker) 
     { 
      _list[_offset] = item; 
      Debug.Write("B " + _offset); 
      _offset += 1; 
      if (_offset == _size) 
       _offset = 0; 
      Debug.Write("A " + _offset + "\n"); 
      return _offset; 
     } 
    } 

    public T[] Dump() 
    { 
     lock (_locker) 
      return _list.ToArray(); 
    } 
} 
+0

Спасибо за решение. В Interlocked.CompareExchange (список ссылок, обновленный, текущий), почему список и текущие разные? и какова цель, которая делает список неустойчивым? – Helic

+0

@Helic 'list' и' current' могут отличаться из-за многопоточности. Представьте, что многие потоки «изменяют» «CopyWriteList» сразу. В большинстве случаев они будут такими же, и CAS преуспеет. [Volatile] (https://msdn.microsoft.com/en-us/library/x13ttww7.aspx) по существу является подсказкой, которая говорит компилятору не оптимизировать переменную для однопоточного использования. – Zer0

+0

, но список является ссылочным типом, должен ли текущий и список хранить одну и ту же ссылку, указывающую на одно и то же местоположение кучи? – Helic

Смежные вопросы