Возможно ли использование нескольких производителей для одного потребителя в режиме без блокировки?

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

Для каждого потока я хочу иметь почтовый ящик, куда другие потоки могут отправлять ему сообщения (но только владелец может удалять сообщения). Это ситуация с несколькими производителями для одного потребителя. Могу ли я сделать это в условиях без блокировки / высокой производительности? (Это во внутреннем цикле гигантской симуляции.)

4 ответа

Решение

Конечно, если у вас есть атомный CompareAndSwap инструкция:

for (i = 0; ; i = (i + 1) % MAILBOX_SIZE)
{
    if ((mailbox[i].owned == false) &&
        (CompareAndSwap(&mailbox[i].owned, true, false) == false))
        break;
}

mailbox[i].message = message;
mailbox[i].ready = true;

После прочтения сообщения потребительский поток просто устанавливает mailbox[i].ready = false; mailbox[i].owned = false; (в этой последовательности).

Очередь с несколькими потребителями без блокировки (MPSC) - один из самых простых алгоритмов без блокировок для реализации.

Самая базовая реализация требует простой односторонне-связанный список (SList) без блокировок только с push () и flush (). Функции доступны в Windows API как InterlockedFlushSList() и InterlockedPushEntrySList(), но их очень легко выполнить самостоятельно.

Несколько элементов Push (), поступающих от производителя, на SList с использованием CAS (блокировка сравнения и обмена).

Single Consumer выполняет flush (), который заменяет начало SList на NULL, используя XCHG (обмен с блокировкой). Потребитель тогда имеет список предметов в обратном порядке.

Чтобы обработать элементы по порядку, вы должны просто перевернуть список, возвращенный функцией flush (), перед его обработкой. Если вы не заботитесь о порядке, вы можете просто просмотреть список сразу, чтобы обработать его.

Две заметки, если вы катите свои собственные функции:

1) Если вы работаете в системе со слабым упорядочением памяти (например, PowerPC), вам необходимо поставить "барьер освобождения памяти" в начале функции push () и "барьер памяти aquire" в конце сброса () функция.

2) Вы можете значительно упростить и оптимизировать функции, потому что проблема ABA с SLists возникает во время функции pop (). Вы не можете иметь проблемы ABA с SList, если вы используете только push () и flush (). Это означает, что вы можете реализовать его как единый указатель, очень похожий на код без блокировки, и нет необходимости в счетчике последовательности, предотвращающем ABA.

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

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

Другие вопросы по тегам