Я бы использовал простую вариацию модели производитель-потребитель. У вас будет две команды, производитель и потребитель, которые имеют целочисленную переменную. У производителя будет System.Threading.Timer
, который срабатывает каждую секунду, и тогда он будет Interlocked.Increment
переменной и вызовет потребителя. Логика потребителя повторно проверяет подачу и Interlocked.Decrement
счетчик, в то время как счетчик больше нуля. Потребительская логика будет защищена Monitor.TryEnter
, который будет обрабатывать повторный вход. Вот пример кода.
public static FeedCheck{
int _count = 0;
static object _consumingSync = new object();
static Threading.Timer _produceTimer;
private static void Consume() {
if (!Monitor.TryEnter(_consumingSync)) return;
try {
while(_count > 0) {
// check feed
Interlocked.Decrement(ref _count);
}
}
finally { Monitor.Exit(_consumingSync); }
}
private static void Produce() {
Interlocked.Increment(ref _count);
Consume();
}
public static void Start() {
// small risk of race condition here, but not necessarily
// be bad if multiple Timers existed for a moment, since only
// the last one will survive.
if (_produceTimer == null) {
_produceTimer = new Threading.Timer(
_ => FeedCheck.Produce(), null, 0, 1000
);
}
}
}
Использование:
FeedCheck.Start();
Хорошим ресурсом по .NET Threading (помимо материалов библиотеки MSDN) является документация Джона Скита, которая включает в себя этот пример производителя-потребителя в разделе "Дополнительные Monitor
методы".
Кстати, настоящий шаблон «производитель-потребитель» вращается вокруг набора рабочих данных, при этом один или несколько потоков производят работу, добавляя данные в эту коллекцию, в то время как один или несколько других потоков-потребителей работают, удаляя данные из этой коллекции. В нашем варианте, приведенном выше, «рабочие данные» - это просто подсчет количества раз, которое нам нужно для немедленной проверки подачи.
(Другой способ сделать это, вместо вызова обратного вызова таймера, состоит в том, чтобы обратный вызов таймера блокировал и передавал Monitor
, который ожидает Consume. В этом случае Consume имеет бесконечный цикл, такой как while(true)
, который вы запускаете один раз в своем собственном потоке. Поэтому нет необходимости поддерживать повторный вход с помощью вызова Monitor.TryEnter
.)