Параллельный связанный список

Я пытаюсь создать связанный список в С++, который позволяет осуществлять одновременный доступ. Очевидно, что использование одного блокировки для этого списка крайне неэффективно, поскольку параллельные области могут обновляться параллельно. Теперь, каковы мои параметры, кроме сохранения блокировки на node?

Кроме того, будет ли более эффективная ставка в этом случае неблокирующей версией? Любые релевантные ссылки, кто-нибудь?

РЕДАКТИРОВАТЬ: Спасибо за ответы людей. Несколько вещей, которые я хотел бы добавить:

  • Как насчет хранения N блокировок для каждого узла M вместо блокировки 1:1: отношение node? Некоторые потоки будут ждать, но это своего рода компромисс. Как вы думаете?
  • Если я намереваюсь найти node в этом связанном списке, похоже, что все мьютексы должны быть заблокированы. Это проблема, поскольку блокировка и разблокировка всех мьютексов занимает много времени. Кто-нибудь имеет лучшую альтернативу?
  • Я открыт для неблокирующих алгоритмов. Но как использовать CAS в хорошем старом С++ без использования сборки? Я слышал, что gcc имеет некоторые атрибуты __sync, которые выполняют аналогичную работу, но не уверены.
  • С неблокирующим подходом, как вы можете найти ссылку в связанном списке?

Ответ 1

Возможно реализовать неблокирующий одноуровневый список, используя блокированные функции:

Блокированные одиночные списки

Я не думаю, что можно реализовать неблокирующий двусвязный список без взаимозависимого сравнения и замены (CAS), который не является широко доступным.

Ответ 2

Связанные списки - это по сути последовательные структуры данных. Независимо от того, какой механизм вы используете для его реализации, интерфейс и ожидаемое поведение подразумевают последовательную логику.

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

Ответ 3

Есть много разных способов, которыми вы могли бы это сделать, в зависимости от того, как вы собираетесь использовать список.

Во-первых, для списка одна блокировка мьютекса не обязательно важна, потому что списки могут поддерживать сращивание. Скажем, у вас есть список символов AF и еще один список BCDE. Вам не нужно блокировать мьютекс, вставлять B, снова блокировать его, вставлять C и т.д. Вы можете вставлять BCDE одновременно между A и F, устанавливая следующий указатель на B и E следующий указатель на F, формируя ABCDEF. Независимо от того, сколько элементов вы хотите вставить сразу, вам нужен только один замок. Это справедливо даже для двусвязного списка. Таким образом, это может быть не узким местом в вашем приложении.

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

Предполагая, что у вас есть один писатель и несколько считывателей, вы можете полностью блокировать блокировку мьютексов, используя атомарные инструкции, предполагая, что они доступны для вашей архитектуры. В GCC они доступны через встроенные функции __sync_ *, а в Visual С++ они доступны через Interlocked *, но если вы застряли с компилятором, который не имеет прямой поддержки для них, вы все равно можете использовать их через встроенную сборку. Писатель будет использовать атомарные инструкции для атомарного набора следующих указателей для патча элементов в и из списка.

Надеюсь, вам это поможет. Чтобы получить глубокий ответ, я предлагаю задать другой вопрос, включая:

  • Есть ли один/несколько читателей?
  • Есть ли один/несколько авторов?
  • Единый или дважды связанный список?
  • Некоторое представление о вашем случае использования.

Ответ 4

Вы уверены, что это проблема, которую стоит решить? Может быть, было бы более полезно инкапсулировать фактическое конечное использование в класс, который обрабатывает блокировку обычного list и обеспечивает поточно-безопасный интерфейс? Таким образом, вы не пытаетесь поставить блокировку на слишком низком уровне (что, как вы предполагали, нелегко решить).

Ответ 5

Как насчет списков пропусков? Посмотрите на реализацию java в "параллельном" пакете.