Является ли копирование всей очереди <T>оператором "=" потокобезопасным (C #) - PullRequest
7 голосов
/ 13 февраля 2012

У меня есть общий Queue<T> (System.Collections.Generic), доступ к которому возможен для записи из одного потока. И это должно быть доступно из другого потока для чтения.

Я не хочу выполнять синхронизацию процессов (включая использование ConcurrentQueue<T>) по соображениям производительности. Поэтому мне пришла в голову идея скопировать всю очередь в другой объект очереди того же типа в потоке чтения. Последующие операции в потоке чтения будут выполнены с копией. Копирование будет выполняться простым оператором =.

Вот некоторый псевдокод:

//Creating main queue
Queue<MyType> queue1 = new Queue<MyType>();

Написание темы:

//Perform writing in the main queue
queue1.Enqueue(object);
...
queue1.Dequeue();

Тема чтения:

//Copy main queue 
Queue<MyType> queue2 = queue1;
//perform all operations in reading thread on queue2

Так безопасен ли такой поток решений?

UPD: Большое спасибо, я не знал, что это просто копирование ссылки. Так есть ли способ скопировать весь объект по значению в поточно-ориентированном режиме?

Ответы [ 5 ]

11 голосов
/ 13 февраля 2012

Queue<T> является ссылочным типом. Поэтому присвоение queue1 queue2 только копирует ссылку, а не саму очередь.

Само назначение является атомарным и, следовательно, потокобезопасным. Доступ к queue1 в одном потоке и queue2 в другом не безопаснее, чем к queue1 из обоих. то есть это небезопасно.

Я считаю, ConcurrentQueue<T> использует методы программирования без блокировки (Interlocked.Exchange и друзья) и работает довольно быстро. Вы должны сначала сравнить его, прежде чем исключать как решение.

Копирование Queue<T>, безусловно, будет медленнее, чем просто использование ConcurrentQueue<T>.


В моей системе с частотой 2,6 ГГц ConcurrentQueue<object> управляет 15 миллионами пар очереди / очереди в секунду по сравнению с 40 миллионами при Queue<object>. Так что Queue<object> примерно в три раза быстрее.

200 циклов ЦП для пары enqueue / dequeue довольно дешевы. Если это узкое место, попробуйте использовать более гранулированные элементы в очереди.

2 голосов
/ 13 февраля 2012

Это не копирует экземпляр Queue.Он только копирует саму ссылку.Копирование ссылки является атомарным, но новая ссылка все равно будет указывать на тот же экземпляр .Изменение экземпляра из нескольких потоков является небезопасным без синхронизации.

1 голос
/ 13 февраля 2012

Краткий ответ - нет, это не потокобезопасно .

Обратите внимание, что вы не копируете саму очередь: вы копируете ссылку на одну очередь.(Присвоение ссылки является атомарным, поэтому ваша строка queue2 = queue1 не является проблемой. Это то, что вы впоследствии делаете с очередью, которая вообще не является поточно-ориентированной.)

0 голосов
/ 13 февраля 2012

Вот что MSDN говорит о безопасности потоков:

Очередь может одновременно поддерживать несколько читателей, если коллекция не изменена. Тем не менее, перечисление в коллекции по сути не является потокобезопасной процедурой. Чтобы гарантировать безопасность потоков во время перечисления, вы можете заблокировать коллекцию во время всего перечисления. Чтобы обеспечить доступ к коллекции из нескольких потоков для чтения и записи, необходимо реализовать собственную синхронизацию.

0 голосов
/ 13 февраля 2012

Копирование коллекции таким способом вызовет только поверхностную копию объекта.Это означает, что он будет копировать только ссылку в ту же очередь.Это потокобезопасно.

Если вы хотите сделать глубокую копию.Посмотрите этот пост может помочь вам выполнить глубокое копирование объекта.Хотя @CodeInChaos имеет действительно хороший момент.Копирование всего объекта таким образом, безусловно, будет медленнее, чем просто использование ConcurentQueue<T>.

...