Генератор последовательных номеров системного уровня Windows / C #?

StackOverflow https://stackoverflow.com/questions/747742

  •  09-09-2019
  •  | 
  •  

Вопрос

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


Разъяснения требований:

  • Не зависит от процесса - на самом деле есть только один процесс, который мог бы получить к этому доступ.
  • Производительность имеет решающее значение!Это используется для регистрации показов на сервере рекламы, скорость которых может достигать 1 кб / сек

Это должно было бы быть одно из следующих:

  • 4-байтовый последовательный номер, который сбрасывает каждый тик
  • 12-байтовый последовательный номер - по сути, добавление 4 байт детализации к дате-времени
Это было полезно?

Решение

Ни один из них не предназначен для этого, но вы могли бы использовать Система.Диагностика.Счетчик производительности.Вы также могли бы использовать реестр, но вам нужно было бы сериализовать доступ на чтение / запись для всех процессов.

System.Diagnostics.PerformanceCounter pc 
    = new System.Diagnostics.PerformanceCounter("SeqCounter", "SeqInstance");
long myVal=pc.Increment();

Редактировать

Чем больше я думаю об этом, тем больше мне кажется, что это могло бы быть хорошим решением.Increment увеличит счетчик на 1 с помощью атомарной операции, которая должна работать во всех процессах системы.

Редактировать

Основываясь на ваших правках, я бы не рекомендовал использовать счетчик производительности.Счетчик производительности был способом координации между несколькими процессами.Я не уверен, как закодирована внутренняя реализация.

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

Система.Нарезание потоков.Взаимосвязано.Увеличивать

К ТВОЕМУ СВЕДЕНИЮ:Если вы используете длинную версию в 32-разрядной системе, она не обязательно будет потокобезопасной.


Редактирование, чтобы показать реализацию, которую я использовал (DS):

public static class Int32Sequencer
{
    private static Int32 lastSequence = Int32.MinValue;
    private static Object lockObject = new Object();
    public static Int32 GetNextSequence()
    {
        lock (lockObject)
        {
            unchecked { lastSequence++; }
            return lastSequence;
        }
    }
}

Другие советы

Идентификатор Guid настолько близок, насколько вы собираетесь получить, но они "уникальны" и не обязательно последовательны.Если вы действительно хотите последовательно выполнять несколько процессов на системном уровне, вам, вероятно, придется выполнить свой собственный.

Редактировать:

Хорошо, итак, исходя из ваших новых требований, я собираюсь предположить:

  1. Только один процесс должен выполнить эту работу
  2. Вы добавляете к базе данных

Итак, вот что я бы порекомендовал:

  1. При запуске процесса запросите в базе данных последнее (наибольшее) значение (0, если таковое не существует).
  2. Используйте простое значение long и increment для каждой строки базы данных.Вы захотите вставлять их пакетно из-за высокой скорости передачи данных.

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

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

Здесь есть статья, в которой приведены некоторые подробности о sql server :Последовательные идентификаторы GUID в SQL Server Этот метод используется для минимизации разделения страниц из-за случайности идентификаторов GUID.Возможно, эта ссылка даст вам какие-то подсказки или идеи.

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

  • Нужно работать со всеми процессами, с одним процессом или с конкретным пользователем?
  • Должны ли числа быть уникальными или просто последовательными?

Исходя из вашего вопроса, похоже, есть несколько разных предметов, которые вы, возможно, ищете.

Нужна последовательная группа чисел для всех процессов в системе

AFAIK, такого сервиса не существует.Один из них должен быть довольно простым в написании, но заставить его работать во всех процессах непросто.

Нужна уникальная последовательная группа чисел для всех процессов в системе

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

Нужен метод получения уникальных значений в системе

Как упоминали несколько других пользователей, лучшим выбором является System.Экземпляр Guid.Вы можете создать новый, используя Guid.NewGuid().Почти для всех целей их можно считать уникальными, но они не являются последовательными.

Мне нравится вариант базы данных просто для безопасности.Однако убедитесь, что вы установили мощный SQL server с выделенной пропускной способностью между вашими серверами и достаточным объемом памяти.Система, похожая на эту, была внедрена в первой компании, в которой я когда-либо работал (еще до того, как я стал программистом), и она была очень хитроумной.Вы можете бороться за то, чтобы масштабировать это.

Другим вариантом было бы внедрить одноэлементную функцию в ваш код...при условии, что его будет вызывать только один домен приложения.МОЖЕТ быть, это немного быстрее, чем выполнять отключение базы данных.Однако, если вы все равно собираетесь вносить этот материал в базу данных....Как насчет объединения этих двух факторов?..Запустите синглтон для ускорения, а затем выполните запись в базу данных, когда позволят ресурсы.

Опять же, если требования к последовательности не настолько сильны, вам лучше всего использовать Guid.

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

Первое, что я бы сделал, это написал тестовую программу, которая породила большое количество потоков, каждый из которых неоднократно вызывал функцию увеличения блокировки, подобную той, которую опубликовал Дэниел Шаффер.Это позволит вам определить порог, при котором ваше приложение начинает работать с перебоями - когда оно тратит больше времени на ожидание Monitor.Enter чем делать что-либо еще.

Если это окажется проблемой - а я готов поспорить, что если объемы, о которых вы говорите, реальны, то так и будет, - тогда вам следует заставить каждый поток поддерживать свой собственный последовательный счетчик, что вы можете сделать, пометив поле счетчика символом ThreadStaticAttribute.Затем вы можете сгенерировать уникальные идентификаторы на основе комбинации идентификатора потока и счетчика.

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

Очевидно, что это нетривиально написать (или, что более важно, протестировать), поэтому я бы определенно рекомендовал сначала доказать, что это необходимо.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top