Многие читатели, один писатель, - можно ли избежать блокировки?
Скажите, что у вас есть список строк в памяти и многопоточная система со многими читателями, но только с одним потоком записи.
В целом, возможно ли реализовать такой тип системы на С# без использования блокировки? Будет ли реализация делать какие-либо предположения о том, как взаимодействуют потоки (или устанавливать ограничения на то, что они могут делать, когда)?
Ответы
Ответ 1
Да. Хитрость заключается в том, чтобы убедиться, что список остается неизменным. Писатель будет делать снимок основной коллекции, изменять моментальный снимок, а затем публиковать снимок для переменной, содержащей ссылку на основную коллекцию. Следующий пример демонстрирует это.
public class Example
{
// This is the immutable master collection.
volatile List<string> collection = new List<string>();
void Writer()
{
var copy = new List<string>(collection); // Snapshot the collection.
copy.Add("hello world"); // Modify the snapshot.
collection = copy; // Publish the snapshot.
}
void Reader()
{
List<string> local = collection; // Acquire a local reference for safe reading.
if (local.Count > 0)
{
DoSomething(local[0]);
}
}
}
Существует несколько предостережений с таким подходом.
- Это работает только потому, что есть один писатель.
- Записываются операции O (n).
- Различные читатели могут одновременно использовать другую версию списка.
- Это довольно опасный трюк. Существуют очень конкретные причины, по которым используется
volatile
, почему локальная ссылка получена на стороне читателя и т.д. Если вы не понимаете этих причин, не используйте шаблон. Слишком много, что может пойти не так.
- Понятие о том, что это поточно-безопасное, является семантическим. Нет, он не будет бросать исключения, взрывать или разрывать целое в пространстве-времени. Но есть и другие способы, с помощью которых эта модель может вызвать проблемы. Знайте, каковы ограничения. Это не чудодейственное средство для любой ситуации.
Из-за вышеуказанных ограничений сценарии, в которых это принесет вам пользу, весьма ограничены. Самая большая проблема заключается в том, что для записи требуется полная копия, поэтому они могут быть медленными. Но, если записи нечасты, тогда это может быть приемлемым.
Я описываю больше шаблонов в моем ответе здесь, а также тот, который безопасен для нескольких авторов.
Ответ 2
Это довольно распространенный запрос для библиотеки потоков для выполнения - такой вид блокировки обычно называют "блокировкой чтения-записи" или некоторым вариантом этой темы. Мне никогда не приходилось использовать реализацию С# специально, но есть одно: http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
Конечно, вы сталкиваетесь с проблемой, что, если читатели всегда будут читать, вы никогда не сможете заставить писателя писать. Полагаю, вам придется самому справиться с этим.
(Хорошо, так что это по-прежнему технически "блокировка", но это не "конструкция блокировки С#", это более сложный объект, специально разработанный для цели, указанной в вопросе. Поэтому я думаю, зависит ли он от правильного ответа о семантике и о том, почему он задавал вопрос.)
Ответ 3
Чтобы избежать блокировок, вы можете рассмотреть параллельные коллекции Microsoft . Эти коллекции предоставляют поточный безопасный доступ к коллекциям объектов как в упорядоченных, так и в неупорядоченных формах. Они используют некоторые аккуратные трюки, чтобы избежать блокировки внутри в максимально возможном количестве случаев.
Ответ 4
Вы также можете использовать новую библиотеку новых библиотек Microsoft: http://blogs.msdn.com/b/bclteam/archive/2012/12/18/preview-of-immutable-collections-released-on-nuget.aspx
Примечание: это полностью отдельно от параллельных коллекций.
Ответ 5
Подход с односвязным списком может использоваться без блокировок при условии, что писатель только вставляет/удаляет либо по голове, либо по хвосту. В любом случае, если вы заранее создадите новый node, вам понадобится только одна атомная операция (head = newHead; или tail.next = newTail), чтобы сделать операцию видимой для читателей.
С точки зрения производительности, вставки и удаления - O (1), а вычисление длины - O (n).