Некоторая комбинация хешей и O (logN) структур должна делать то, что вы просите.
У меня возникает соблазн поспорить с тем, как вы анализируете проблему. В своем комментарии выше вы говорите
Потому что обновление будет происходить очень и очень часто. Допустим, мы отправляем M сообщений на соединение, тогда общее время становится O (MNlogN), что довольно много. - Trustin Lee (6 часов назад)
, что абсолютно правильно, насколько это возможно. Но большинство людей, которых я знаю, сконцентрировались бы на стоимости за сообщение , на теории, что, поскольку у вашего приложения есть все больше и больше работы, очевидно, что для этого потребуется больше ресурсов.
Так что, если в вашем приложении одновременно открыт миллиард сокетов (это действительно вероятно?), Стоимость вставки составляет всего около 60 сравнений на сообщение.
Держу пари, что это преждевременная оптимизация: вы фактически не измерили узкие места в вашей системе с помощью инструмента анализа производительности, такого как CodeAnalyst или VTune.
В любом случае, вероятно, существует бесконечное количество способов сделать то, что вы просите, если вы просто решите, что ни одна структура не будет делать то, что вы хотите, и вам понадобится некоторая комбинация сильных и слабых сторон различных алгоритмов.
Одна из возможностей - разделить домен сокетов N на некоторое количество сегментов размера B, а затем хэшировать каждый сокет в одно из этих (N / B) сегментов. В этом сегменте находится куча (или что-то еще) со временем обновления O (log B). Если верхняя граница N не установлена заранее, но может варьироваться, то вы можете динамически создавать больше сегментов, что добавляет небольшие сложности, но, безусловно, выполнимо.
В худшем случае сторожевой таймер должен искать (N / B) очереди на предмет истечения, но я предполагаю, что сторожевой таймер не требуется для уничтожения незанятых сокетов в любом конкретном порядке!
То есть, если в последнем интервале времени 10 сокетов простаивали, ему не нужно искать в этом домене тот, который истек первый тайм-аут, иметь дело с ним, затем находить тот, который истек второй тайм-аут и т. Д. должен просканировать (N / B) набор сегментов и перечислить все тайм-ауты.
Если вас не устраивает линейный массив сегментов, вы можете использовать приоритетную очередь очередей, но вы хотите избежать обновления этой очереди в каждом сообщении, иначе вы вернетесь туда, откуда начали. Вместо этого определите время, которое меньше фактического времени ожидания. (Скажем, 3/4 или 7/8 от этого), и вы помещаете очередь низкого уровня в очередь высокого уровня только в том случае, если ее наибольшее время превышает это значение.
И, рискуя указать очевидное, вы не хотите, чтобы ваши очереди включались в истекшее время. Ключи должны быть время начала . Для каждой записи в очереди истекшее время должно постоянно обновляться, но время начала каждой записи не меняется.