Параллельный дизайн структуры данных

Вы можете добавлять элементы HTML прямо в typeString(). Это означает, что вы можете обернуть весь текст интервалами и добавить имя класса или добавить стили непосредственно внутри <span/>.

var app = document.getElementById('app');

var typewriter = new Typewriter(app, {
  wrapperClassName: 'test',
  delay: 50,
  loop: true,
});

typewriter
  .typeString('<strong>We are: </strong>')
  .pauseFor(550)
  .typeString('<span style="color:green;">transportation</span>')
  .pauseFor(550)
  .deleteChars(14)
  .pauseFor(900)
  .typeString('<span style="color:blue;">auto dealers</span>')
  .pauseFor(550)
  .deleteChars(12)
  .pauseFor(900)
  .typeString('<span style="color:purple;">realtors</span>')
  .pauseFor(550)
  .deleteChars(8)
  .pauseFor(900)
  .start();
<script src="https://unpkg.com/typewriter-effect/dist/core.js"></script>
<div id="app"></div>

Надеюсь, это помогает,

14
задан Rob Walker 4 November 2008 в 17:44
поделиться

10 ответов

Лично, я довольно люблю персистентные неизменные структуры данных в высоко параллельных ситуациях. Я не знаю ни о ком специально для C++, но Богатое Пятно создало некоторых превосходных (и горячо быстро) неизменные структуры данных в Java для Clojure. Конкретно: вектор, хеш-таблица и hashset. Они не слишком тверды к порту, таким образом, можно хотеть рассмотреть одного из тех.

Для разработки немного больше персистентные неизменные структуры данных действительно решают много проблем, связанных с параллелизмом. Поскольку сама структура данных неизменна, нет проблемы с несколькими потоками, читающими/выполняющими итерации одновременно (пока это - итератор константы). "Запись" может также быть асинхронной, потому что она действительно не пишет в существующую структуру, а скорее создает новую версию той структуры, которая включает новый элемент. Эта операция сделана эффективной ( O (1) во всех структурах Пятна) тем, что Вы на самом деле не копируете все. Каждая новая версия совместно использует большую часть своей структуры со старой версией. Это делает вещи большей памятью эффективный, а также существенно улучшение производительности по простому методу копии на записи.

С неизменными структурами данных, единственное время, где на самом деле необходимо синхронизироваться, находится в фактической записи в ссылочную ячейку. Так как доступ к памяти является атомарным, даже это может обычно быть свободно от блокировок. Единственный протест здесь - Вы, мог бы потерять данные между потоками (условия состязания). Структура данных никогда не будет повреждаться из-за параллелизма, но это не означает, что непоследовательные результаты невозможны в ситуациях, где два потока создают новую версию структуры на основе старого сингла и пытаются записать свои результаты (один из них "победит", и изменения других будут потеряны). Для решения этой проблемы Вы или должны иметь блокировку для "записи операций" или использовать своего рода STM. Мне нравится второй подход для простоты, и пропускная способность в системах низкой коллизии (записи идеально не блокируются и чтения никогда блок), но любой будет работать.

Вы задали сложный вопрос, один, для которого нет действительно хорошего ответа. Безопасные от параллелизма структуры данных трудно записать, особенно когда они должны быть изменяемыми. Абсолютно свободная от блокировок архитектура доказуемо невозможна в присутствии общего состояния, таким образом, можно хотеть разочароваться в том требовании. Лучшее, которое можно сделать, минимизируют требуемую блокировку, следовательно неизменные структуры данных.

12
ответ дан 1 December 2019 в 10:19
поделиться

У Вас есть 3 типа задач:

  1. повторение (медленное)
  2. вставка (быстро)
  3. , удаление (быстро)

, Если около непротиворечивости достаточно хорошо затем, отслеживает # активных итеративных задач.

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

, Как только последнее повторение, если законченный поставленный в очередь процесс вставляет и удаляет.

, Если итеративный запрос входит, в то время как вставляет или удаляет, находятся на рассмотрении, затем ставят его в очередь.

, Если итеративный запрос входит, в то время как существуют только повторения, работающие просто, имеют его, идут и выполняют итерации.

необходимо все еще записать повторение, чтобы быть максимально быстро путем создания копии данных, которых Вы выполняете итерации и затем обрабатываете те данные в клиенте, если фактическая обработка данных занимает намного больше времени, чем само повторение.

я реализовал бы основной набор с хеш-таблицей, или stl:map мог бы даже быть достаточно быстрым. Вставьте/Удалите запросы, мог быть поставлен в очередь в списке.

1
ответ дан 1 December 2019 в 10:19
поделиться

Единственным путем я думаю, что это достижимо, через что-то подобное протоколу параллелизма мультиверсии, используемому в базах данных, таких как oracle/postgresql и т.д. Это гарантирует, что читатели не блокируют читателей, устройства записи не блокирует читателей, но блок устройств записи только те устройства записи, которые обновляют ту же часть данных. Это свойство устройств записи, блокирующих устройство (устройства) записи, которые обновляют ту же часть данных, важно в параллельном мире программирования, иначе несоответствия данных/системы возможны. Для каждой операции записи к структуре данных Вы берете снимок структуры данных или по крайней мере части узлов структуры данных, затронутых операцией записи к различному местоположению в памяти прежде, чем сделать запись. Таким образом, когда запись происходит, поток читателя запрашивает считать часть данных из части устройства записи, Вы всегда обращаетесь к последнему снимку и выполняете итерации по тому снимку, там путем обеспечения последовательного представления данных всем читателям. Снимок является дорогостоящим, так как они используют больше памяти, но да для Вашего данного требования, эта техника является правильной для движения для. И да используют блокировки (взаимное исключение/семафор/спин-блокировка) для защиты операции записи от других потоков/процессов устройства записи, бывших должных обновить ту же часть данных.

1
ответ дан 1 December 2019 в 10:19
поделиться

Ну, чтобы быть ориентированными на многопотоковое исполнение, Вы оказываетесь перед необходимостью блокировать что-то в какой-то момент. Одна ключевая вещь состоит в том, чтобы удостовериться, что объекты в Вашем репозитории могут быть заблокированы отдельно от самой структуры репозитория: т.е. не имейте ссылки _next или чего-либо вида в данных, которые Вы храните. Таким образом, операции чтения могут заблокировать содержание объектов, не блокируя структуру репозитория.

Эффективная вставка легка: связанный список, неотсортированные массивы, хеш-таблицы вся работа хорошо. Эффективное удаление более трудно, так как это вовлекает нахождение удаленной вещи в репозиторий. Howerver, для необработанной простоты и скорости, связанный список является хорошим выбором. Удаления могут быть отложены в течение ненапряженного времени и объектов, просто отмеченных как "неактивные"? Затем стоимость нахождения/удаления не так ограничивает.

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

0
ответ дан 1 December 2019 в 10:19
поделиться

Извинения за двойной ответ...

, Так как записи довольно редки, действительно необходимо рассмотреть использование STM вместо блокировки. STM является формой оптимистической блокировки, что означает, что это в большой степени смещается в производительности к системам без коллизий (иначе меньше записей). В отличие от этого, пессимистическая блокировка (lock-write-unlock) оптимизирована для тяжелых коллизией систем (иначе много записей). Единственная выгода с STM, это почти требует, чтобы Вы использовали неизменные структуры данных в ячейках TVar, иначе целая система ломается. Лично, я не думаю, что это - проблема, так как достойная неизменная структура данных будет настолько же быстрой как изменяемая (см. мой другой ответ), но это достойно рассмотрения.

4
ответ дан 1 December 2019 в 10:19
поделиться

Связанные списки являются определенно ответом здесь. Вставка и удаление в O (1), повторение от одного узла до следующего в O (1) и устойчивость через операции. std::list гарантии все они, включая это все итераторы допустимы, если элемент не удален из списка (это включает указатели и ссылки на элементы). Для блокировки Вы могли просто перенести список в класс блокировки, или Вы могли записать свой собственный класс списка (Вы не сможете использовать std::list в этом случае, который поддерживает основанную на узле блокировку - например, можно заблокировать вниз определенные области списка для использования, в то время как другие потоки выполняют операции на различных областях. То, которое Вы используете, в основном зависит от типа параллельного доступа, который Вы ожидаете - если несколько операций на различных частях списка будут действительно распространены, запишите свое собственное, но помните помещение взаимоисключающего объекта в каждый узел который не эффективен пространством.

6
ответ дан 1 December 2019 в 10:19
поделиться

Я думаю, что связанный список должен ответить на Ваши требования. Обратите внимание, что можно заблокировать только узлы, которые изменяются (т.е. удалил/добавил), таким образом, читатели большую часть времени смогут работать во всем параллелизме с устройствами записи. Этот подход требует блокировки на узел связанного списка, однако это не необходимость. Можно было ограничить сумму блокировок, и затем несколько узлов будут отображены на той же блокировке. Т.е. наличие массива блокировок N и узлов пронумеровало 0.. M Вы может использовать блокировку (% NodeId N) для блокировки этого узла. Это могут быть блокировки чтения-записи, и путем управления суммой блокировок, можно управлять суммой параллелизма.

1
ответ дан 1 December 2019 в 10:19
поделиться

Если Вам не нужен порядок сортировки, не используйте красное/черное дерево или что-либо еще это по сути виды.

Ваш вопрос не хорошо указан достаточно w.r.t к взаимодействию между чтениями и записями. Было бы хорошо, если "чтение" реализовано lock+copy+unlock, и затем используйте новую копию?

можно хотеть читать о seqlocks в http://en.wikipedia.org/wiki/Seqlock , и на "блокировке бесплатные" процессы в целом - хотя, можно хотеть ослабить требования как можно больше - свободная от блокировок реализация хеш-таблицы является основным обязательством.

1
ответ дан 1 December 2019 в 10:19
поделиться

FWIW, это тривиально решить, если у вас есть сборщик мусора. В F #, например, вы можете просто использовать изменяемую ссылку на связанный список или чисто функциональную карту (сбалансированное двоичное дерево) без каких-либо блокировок. Это работает, потому что структуры данных неизменяемы, а запись ссылки (для обновления после записи) является атомарной, поэтому параллельные читатели гарантированно увидят старую или новую структуру данных, но никогда не повредят. Если у вас несколько программ записи, вы можете сериализовать их.

Однако это намного сложнее решить на C ++ ...

0
ответ дан 1 December 2019 в 10:19
поделиться

I'm not sure if anybody has mentioned this, but I would take inspiration from Java's ConcurrentHashMap. It offers traversal, retrieval and insertion without locking or waiting. The only lock occurs once you've found a bucket of data corresponding to the hash key and you're traversing that bucket (i.e. you ONLY lock the bucket not the actual hash map). "Instead of a single collection lock, ConcurrentHashMap uses a fixed pool of locks that form a partition over the collection of buckets."

You can find more details on the actual implementation here. I believe that all of the things shown in the implementation can be just as easily done with C++.

So let's go through your list of requirements:

1. High throughput. CHECK
2. Thread safe. CHECK
3. Efficient inserts happen in O(1). CHECK
4. Efficient removal (with no data races or locks). CHECK
5. VERY efficient traversal. CHECK
6. Does not lock or wait. CHECK
7. Easy on the memory. CHECK
8. It is scalable (just increase the lock pool). CHECK

Here is an example of a map Entry:

protected static class Entry implements Map.Entry {
    protected final Object key;
    protected volatile Object value;
    protected final int hash;
    protected final Entry next;
    ...
}

Note that the value is volatile, so when we're removing an Entry we set the value to NULL which is automatically visible to and any other thread that attempts to read the value.

1
ответ дан 1 December 2019 в 10:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: