Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Следите за ссылкой на данные (сколько / кто) в многопоточности

Автор вопроса: naveenhegdeОтветов: 3

Я столкнулся с проблемой многопоточности. Модель многопоточности - 1 производитель - N потребитель.

Производитель создает данные (символьные данные размером около 200 байт каждый), помещает их в кеш фиксированного размера (например, 2Mil). Данные не относятся ко всем потокам. Он применяет фильтр (настроенный) и определяет, что ни один из потоков не подходит для производимых данных.

Производитель помещает указатель на данные в очередь подходящих потоков (только указатель на данные, чтобы избежать копирования данных). Потоки будут удаляться из очереди и отправлять их по TCP/IP своим клиентам.

Проблема: из-за того, что нескольким потокам передается только указатель на данные, когда кеш заполняется, Produces хочет удалить первый элемент (старый). возможность того, что любой поток все еще ссылается на данные.

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

class InUseCounter
{
    int           m_count;
    set<thread_t> m_in_use_threads;
    Mutex         m_mutex;
    Condition     m_cond;

public:
    // This constructor used by Producer
    InUseCounter(int count, set<thread_t> tlist)
    {
        m_count          = count;
        m_in_use_threads = tlist;
    }

    // This function is called by each threads
    // When they are done with the data,
    // Informing that I no longer use the reference to the data.
    void decrement(thread_t tid)
    {
        Gaurd<Mutex> lock(m_mutex);
        --m_count;
        m_in_use_threads.erease(tid);
    }

    int get_count() const { return m_count; }
};

мастер-чаш

map<seqnum, Data>
              |
              v
             pair<CharData, InUseCounter>

Когда производитель удаляет элемент, он проверяет счетчик, больше 0, он отправляет действие, чтобы освободить ссылку на потоки в наборе m_in_use_threads.

Вопрос

  1. Если в главном кеше есть записи 2Mil, будет равное количество InUseCounter, поэтому переменные Mutex, целесообразно ли иметь переменную mutex 2Mil в одном процессе.
  2. Наличие большой единой структуры данных для поддержки InUseCounter приведет к увеличению времени блокировки для поиска и уменьшения.
  3. Что было бы лучшей альтернативой моему подходу к поиску ссылок и у кого есть ссылки с очень меньшим временем блокировки.

Заранее спасибо вам за советы.

c++multithreadingmutexcontext-switch

Ответы

Ответ 1 · рейтинг 4 · MSN

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

Кстати, это похоже на то, как работает структура Disruptor. Это высокопроизводительная среда параллелизма Java(!) для высокочастотной торговли. Да, я сказал "высокопроизводительная Java" и "параллелизм" в одном предложении. Существует много ценных сведений о проектировании и реализации высокопроизводительного параллелизма.

Ответ 2 · рейтинг 1 · Matthieu M.

Поскольку у вас уже есть очередь Producer->Consumer, одна очень простая система состоит в наличии очереди "обратной связи" (Consumer->Producer).

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

Таким образом, только Производитель когда-либо касается внутренностей кэша, и никакой синхронизации там не требуется: нужно синхронизировать только очереди.

Ответ 3 · stefaanv

  1. Да, 2000000 мьютексов - это перебор.
  2. 1 большая структура будет заблокирована дольше, но потребует намного меньше запирания/разблокировки.
  3. лучшим подходом было бы использование интеллектуальных указателей shared_ptr: они, похоже, созданы специально для этого. Вы не проверяете счетчик самостоятельно, вы просто подчищаете свой указатель. shared_ptr является потокобезопасным, а не данными, на которые он указывает, но для 1 производителя (записи) / N потребителей (читателей) это не должно быть проблемой.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы