С# запускать поток каждые X минут, но только если этот поток еще не запущен
У меня есть программа на С#, которая должна отправлять поток каждые X минут, но только если ранее отправленный поток (с X минут) назад еще не запущен.
Обычный простой Timer
не будет работать (поскольку он отправляет событие каждые X минут независимо от того, закончил ли ранее обработанный процесс еще).
Процесс, который будет отправлен, сильно зависит от времени, необходимого для выполнения его задачи - иногда это может занять несколько секунд, иногда это может занять несколько часов. Я не хочу снова запускать процесс, если он все еще обрабатывается с момента последнего запуска.
Может ли кто-нибудь предоставить примерный пример кода С#?
Ответы
Ответ 1
По моему мнению, путь в этой ситуации - использовать класс System.ComponentModel.BackgroundWorker
, а затем просто проверить его свойство IsBusy
каждый раз, когда вы хотите отправить (или нет) новый поток. Код довольно прост; вот пример:
class MyClass
{
private BackgroundWorker worker;
public MyClass()
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
Timer timer = new Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(!worker.IsBusy)
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//whatever You want the background thread to do...
}
}
В этом примере я использовал System.Timers.Timer
, но я считаю, что он также должен работать с другими таймерами. Класс BackgroundWorker
также поддерживает отчет о проделанной работе и отмену и использует управляемую событиями модель связи с потоком диспетчеризации, поэтому вам не нужно беспокоиться об изменчивых переменных и т.п....
ИЗМЕНИТЬ
Здесь более подробный пример, включающий отмена и отчет о проделанной работе:
class MyClass
{
private BackgroundWorker worker;
public MyClass()
{
worker = new BackgroundWorker()
{
WorkerSupportsCancellation = true,
WorkerReportsProgress = true
};
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
Timer timer = new Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if(!worker.IsBusy)
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker w = (BackgroundWorker)sender;
while(/*condition*/)
{
//check if cancellation was requested
if(w.CancellationPending)
{
//take any necessary action upon cancelling (rollback, etc.)
//notify the RunWorkerCompleted event handler
//that the operation was cancelled
e.Cancel = true;
return;
}
//report progress; this method has an overload which can also take
//custom object (usually representing state) as an argument
w.ReportProgress(/*percentage*/);
//do whatever You want the background thread to do...
}
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//display the progress using e.ProgressPercentage and/or e.UserState
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
//do something
}
else
{
//do something else
}
}
}
Затем, чтобы отменить дальнейшее выполнение, просто вызовите worker.CancelAsync()
. Обратите внимание, что это полностью обработанный пользователем механизм отмены (он не поддерживает прерывание нити или что-то подобное из коробки).
Ответ 2
Вы можете просто сохранить volatile bool для достижения того, что вы спросили:
private volatile bool _executing;
private void TimerElapsed(object state)
{
if (_executing)
return;
_executing = true;
try
{
// do the real work here
}
catch (Exception e)
{
// handle your error
}
finally
{
_executing = false;
}
}
Ответ 3
Вы можете отключить и включить ваш таймер в прошедшем обратном вызове.
public void TimerElapsed(object sender, EventArgs e)
{
_timer.Stop();
//Do Work
_timer.Start();
}
Ответ 4
Вы можете просто использовать System.Threading.Timer
и просто установить Timeout
в Infinite
, прежде чем обрабатывать свои данные/метод, а затем, когда он завершит перезапуск Timer
, готов к следующему вызову.
private System.Threading.Timer _timerThread;
private int _period = 2000;
public MainWindow()
{
InitializeComponent();
_timerThread = new System.Threading.Timer((o) =>
{
// Stop the timer;
_timerThread.Change(-1, -1);
// Process your data
ProcessData();
// start timer again (BeginTime, Interval)
_timerThread.Change(_period, _period);
}, null, 0, _period);
}
private void ProcessData()
{
// do stuff;
}
Ответ 5
Если вы хотите, чтобы обратный вызов таймера загорелся в фоновом потоке, вы можете использовать System.Threading.Timer. Этот класс Timer позволяет "Определить Timeout.Infinite
, чтобы отключить периодическую сигнализацию". как часть конструктора который заставляет таймер запускаться только один раз.
Затем вы можете создать новый таймер, когда ваш первый обратный вызов по таймеру срабатывает и завершается, предотвращая планирование нескольких таймеров, пока вы не будете готовы к ним.
Преимущество здесь заключается в том, что вы не создаете таймеры, а затем отменяете их повторно, так как вы никогда не планируете больше, чем ваше "следующее событие" за раз.
Ответ 6
Существует не менее 20 различных способов выполнения этого, от использования таймера и семафора, к изменчивым переменным, к использованию TPL, к использованию инструмента планирования с открытым исходным кодом, такого как Quartz и т.д.
Создание потока - дорогое упражнение, поэтому почему бы просто не создать ОДИН и оставить его в фоновом режиме, так как он будет тратить большую часть своего времени IDLE, это не приводит к реальному истощению системы. Периодически просыпайтесь и делайте работу, затем возвращайтесь спать на время. Независимо от того, сколько времени займет задача, вы всегда будете ждать, по крайней мере, времени ожидания waitForWork после завершения перед запуском нового.
//wait 5 seconds for testing purposes
static TimeSpan waitForWork = new TimeSpan(0, 0, 0, 5, 0);
static ManualResetEventSlim shutdownEvent = new ManualResetEventSlim(false);
static void Main(string[] args)
{
System.Threading.Thread thread = new Thread(DoWork);
thread.Name = "My Worker Thread, Dude";
thread.Start();
Console.ReadLine();
shutdownEvent.Set();
thread.Join();
}
public static void DoWork()
{
do
{
//wait for work timeout or shudown event notification
shutdownEvent.Wait(waitForWork);
//if shutting down, exit the thread
if(shutdownEvent.IsSet)
return;
//TODO: Do Work here
} while (true);
}
Ответ 7
Вы можете использовать System.Threading.Timer. Trick - установить только начальное время. Начальное время устанавливается снова, когда предыдущий интервал завершен или когда задание завершено (это произойдет, когда задание займет больше времени, чем интервал). Вот пример кода.
class Program
{
static System.Threading.Timer timer;
static bool workAvailable = false;
static int timeInMs = 5000;
static object o = new object();
static void Main(string[] args)
{
timer = new Timer((o) =>
{
try
{
if (workAvailable)
{
// do the work, whatever is required.
// if another thread is started use Thread.Join to wait for the thread to finish
}
}
catch (Exception)
{
// handle
}
finally
{
// only set the initial time, do not set the recurring time
timer.Change(timeInMs, Timeout.Infinite);
}
});
// only set the initial time, do not set the recurring time
timer.Change(timeInMs, Timeout.Infinite);
}
Ответ 8
Использование PeriodicTaskFactory из моего сообщения здесь
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task task = PeriodicTaskFactory.Start(() =>
{
Console.WriteLine(DateTime.Now);
Thread.Sleep(5000);
}, intervalInMilliseconds: 1000, synchronous: true, cancelToken: cancellationTokenSource.Token);
Console.WriteLine("Press any key to stop iterations...");
Console.ReadKey(true);
cancellationTokenSource.Cancel();
Console.WriteLine("Waiting for the task to complete...");
Task.WaitAny(task);
Вывод ниже показывает, что, хотя интервал установлен в 1000 миллисекунд, каждая итерация не начинается, пока работа действия задачи не будет завершена. Это выполняется с использованием необязательного параметра synchronous: true
.
Press any key to stop iterations...
9/6/2013 1:01:52 PM
9/6/2013 1:01:58 PM
9/6/2013 1:02:04 PM
9/6/2013 1:02:10 PM
9/6/2013 1:02:16 PM
Waiting for the task to complete...
Press any key to continue . . .
UPDATE
Если вы хотите, чтобы поведение "пропущенного события" с PeriodicTaskFactory просто не использовало синхронный параметр и реализовывало Monitor.TryEnter, как то, что Боб сделал здесь fooobar.com/questions/104558/...
Task task = PeriodicTaskFactory.Start(() =>
{
if (!Monitor.TryEnter(_locker)) { return; } // Don't let multiple threads in here at the same time.
try
{
Console.WriteLine(DateTime.Now);
Thread.Sleep(5000);
}
finally
{
Monitor.Exit(_locker);
}
}, intervalInMilliseconds: 1000, synchronous: false, cancelToken: cancellationTokenSource.Token);
Самое приятное в PeriodicTaskFactory
заключается в том, что возвращается Задача, которая может использоваться со всеми TPL API, например. Task.Wait
, продолжения и т.д.
Ответ 9
Почему бы не использовать таймер с Monitor.TryEnter()
? Если OnTimerElapsed()
вызывается еще раз до того, как закончится предыдущий поток, он будет просто отброшен, и повторная попытка не повторится, пока таймер не заработает снова.
private static readonly object _locker = new object();
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
if (!Monitor.TryEnter(_locker)) { return; } // Don't let multiple threads in here at the same time.
try
{
// do stuff
}
finally
{
Monitor.Exit(_locker);
}
}
Ответ 10
Вы можете остановить таймер перед заданием и запустить его снова после завершения задачи, это может сделать ваш прием периодическим с периодическим периодом времени.
public void myTimer_Elapsed(object sender, EventArgs e)
{
myTimer.Stop();
// Do something you want here.
myTimer.Start();
}
Ответ 11
Если вы правильно поняли, вы просто хотите убедиться, что ваш поток не запущен, прежде чем отправлять другой поток. Скажем, у вас есть поток, определенный в вашем классе.
private System.Threading.Thread myThread;
Вы можете сделать:
//inside some executed method
System.Threading.Timer t = new System.Threading.Timer(timerCallBackMethod, null, 0, 5000);
затем добавьте callBack так:
private void timerCallBackMethod(object state)
{
if(myThread.ThreadState == System.Threading.ThreadState.Stopped || myThread.ThreadState == System.Threading.ThreadState.Unstarted)
{
//dispatch new thread
}
}
Ответ 12
У этого вопроса уже есть ряд хороших ответов, в том числе несколько более новый, основанный на некоторых функциях в TPL. Но я чувствую недостаток здесь:
- Решение на основе TPL a) на самом деле не содержится здесь полностью, а скорее относится к другому ответу; b) не показывает, как можно использовать
async
/await
для реализации механизма синхронизации в одном метод и в) ссылочная реализация довольно сложна, что несколько затуманивает лежащую в основе соответствующую точку в этом конкретном вопросе.
- Исходный вопрос здесь несколько расплывчатый относительно конкретных параметров желаемой реализации (хотя некоторые из них уточняются в комментариях). В то же время другие читатели могут иметь схожие, но не идентичные потребности, и ни один ответ не отвечает множеству вариантов дизайна, которые могут быть желательными.
- Мне особенно нравится выполнять периодическое поведение, используя
Task
и async
/await
таким образом, из-за упрощения кода. Функция async
/await
, в частности, настолько ценна при использовании кода, который в противном случае был бы разрушен деталями реализации продолжения/обратного вызова и сохранением его естественной линейной логики одним способом. Но здесь нет ответа на эту простоту.
Итак, с этим обоснованием мотивируя меня добавить еще один ответ на этот вопрос & hellip;
Для меня первое, что нужно учитывать, - "какое точное поведение здесь требуется?" Вопрос здесь начинается с базовой предпосылки: задача времени, инициированная таймером, не должна запускаться одновременно, даже если задача занимает больше времени, чем период таймера. Но есть несколько способов, по которым может быть выполнено предположение, в том числе:
- Не запускайте таймер во время выполнения задачи.
- Запустите таймер (этот и остальные параметры, которые я здесь представляю, предполагают, что таймер продолжает работать во время выполнения задачи), но если задача занимает больше времени, чем таймер, запустите задачу снова сразу после нее завершено с предыдущего таймера таймера.
- Только инициировать выполнение задачи по таймеру. Если задача занимает больше времени, чем период таймера, не запускайте новую задачу, пока выполняется текущая, и даже после завершения текущего не запускайте новую, пока не будет выбран следующий таймер.
- Если задача занимает больше времени, чем интервал таймера, не только запускать задачу сразу после ее завершения, но и запускать ее столько раз, сколько необходимо, пока задача не будет "догнана". То есть со временем приложите максимум усилий для выполнения задачи один раз для каждого таймера.
На основе комментариев у меня создалось впечатление, что параметр # 3 наиболее точно соответствует исходному запросу OP, хотя похоже, что вариант № 1, возможно, тоже будет работать. Но варианты # 2 и # 4 могут быть предпочтительнее для кого-то другого.
В следующем примере кода я реализовал эти параметры с помощью пяти разных методов (два из них реализуют вариант № 3, но несколько по-разному). Конечно, можно было бы выбрать подходящую реализацию для одной потребности. Вероятно, вам не нужны все пять в одной программе!:)
Ключевым моментом является то, что во всех этих реализациях они, естественно, очень просто, выполняют задачу в течение периода, но не одновременно. То есть они эффективно реализуют модель исполнения на основе таймера, гарантируя, что задача выполняется только по одному потоку за один раз за первичный запрос вопроса.
В этом примере также показано, как CancellationTokenSource
можно использовать для прерывания задачи периода, используя await
, чтобы обработать модель на основе исключений простым, простым способом.
class Program
{
const int timerSeconds = 5, actionMinSeconds = 1, actionMaxSeconds = 7;
static Random _rnd = new Random();
static void Main(string[] args)
{
Console.WriteLine("Press any key to interrupt timer and exit...");
Console.WriteLine();
CancellationTokenSource cancelSource = new CancellationTokenSource();
new Thread(() => CancelOnInput(cancelSource)).Start();
Console.WriteLine(
"Starting at {0:HH:mm:ss.f}, timer interval is {1} seconds",
DateTime.Now, timerSeconds);
Console.WriteLine();
Console.WriteLine();
// NOTE: the call to Wait() is for the purpose of this
// specific demonstration in a console program. One does
// not normally use a blocking wait like this for asynchronous
// operations.
// Specify the specific implementation to test by providing the method
// name as the second argument.
RunTimer(cancelSource.Token, M1).Wait();
}
static async Task RunTimer(
CancellationToken cancelToken, Func<Action, TimeSpan, Task> timerMethod)
{
Console.WriteLine("Testing method {0}()", timerMethod.Method.Name);
Console.WriteLine();
try
{
await timerMethod(() =>
{
cancelToken.ThrowIfCancellationRequested();
DummyAction();
}, TimeSpan.FromSeconds(timerSeconds));
}
catch (OperationCanceledException)
{
Console.WriteLine();
Console.WriteLine("Operation cancelled");
}
}
static void CancelOnInput(CancellationTokenSource cancelSource)
{
Console.ReadKey();
cancelSource.Cancel();
}
static void DummyAction()
{
int duration = _rnd.Next(actionMinSeconds, actionMaxSeconds + 1);
Console.WriteLine("dummy action: {0} seconds", duration);
Console.Write(" start: {0:HH:mm:ss.f}", DateTime.Now);
Thread.Sleep(TimeSpan.FromSeconds(duration));
Console.WriteLine(" - end: {0:HH:mm:ss.f}", DateTime.Now);
}
static async Task M1(Action taskAction, TimeSpan timer)
{
// Most basic: always wait specified duration between
// each execution of taskAction
while (true)
{
await Task.Delay(timer);
await Task.Run(() => taskAction());
}
}
static async Task M2(Action taskAction, TimeSpan timer)
{
// Simple: wait for specified interval, minus the duration of
// the execution of taskAction. Run taskAction immediately if
// the previous execution too longer than timer.
TimeSpan remainingDelay = timer;
while (true)
{
if (remainingDelay > TimeSpan.Zero)
{
await Task.Delay(remainingDelay);
}
Stopwatch sw = Stopwatch.StartNew();
await Task.Run(() => taskAction());
remainingDelay = timer - sw.Elapsed;
}
}
static async Task M3a(Action taskAction, TimeSpan timer)
{
// More complicated: only start action on time intervals that
// are multiples of the specified timer interval. If execution
// of taskAction takes longer than the specified timer interval,
// wait until next multiple.
// NOTE: this implementation may drift over time relative to the
// initial start time, as it considers only the time for the executed
// action and there is a small amount of overhead in the loop. See
// M3b() for an implementation that always executes on multiples of
// the interval relative to the original start time.
TimeSpan remainingDelay = timer;
while (true)
{
await Task.Delay(remainingDelay);
Stopwatch sw = Stopwatch.StartNew();
await Task.Run(() => taskAction());
long remainder = sw.Elapsed.Ticks % timer.Ticks;
remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder);
}
}
static async Task M3b(Action taskAction, TimeSpan timer)
{
// More complicated: only start action on time intervals that
// are multiples of the specified timer interval. If execution
// of taskAction takes longer than the specified timer interval,
// wait until next multiple.
// NOTE: this implementation computes the intervals based on the
// original start time of the loop, and thus will not drift over
// time (not counting any drift that exists in the computer clock
// itself).
TimeSpan remainingDelay = timer;
Stopwatch swTotal = Stopwatch.StartNew();
while (true)
{
await Task.Delay(remainingDelay);
await Task.Run(() => taskAction());
long remainder = swTotal.Elapsed.Ticks % timer.Ticks;
remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder);
}
}
static async Task M4(Action taskAction, TimeSpan timer)
{
// More complicated: this implementation is very different from
// the others, in that while each execution of the task action
// is serialized, they are effectively queued. In all of the others,
// if the task is executing when a timer tick would have happened,
// the execution for that tick is simply ignored. But here, each time
// the timer would have ticked, the task action will be executed.
//
// If the task action takes longer than the timer for an extended
// period of time, it will repeatedly execute. If and when it
// "catches up" (which it can do only if it then eventually
// executes more quickly than the timer period for some number
// of iterations), it reverts to the "execute on a fixed
// interval" behavior.
TimeSpan nextTick = timer;
Stopwatch swTotal = Stopwatch.StartNew();
while (true)
{
TimeSpan remainingDelay = nextTick - swTotal.Elapsed;
if (remainingDelay > TimeSpan.Zero)
{
await Task.Delay(remainingDelay);
}
await Task.Run(() => taskAction());
nextTick += timer;
}
}
}
Одно последнее замечание: я наткнулся на этот Q & A после того, как он повторил его как дубликат другого вопроса. В этом другом вопросе, в отличие от этого, ОП особо отметил, что они используют класс System.Windows.Forms.Timer
. Конечно, этот класс используется в основном потому, что он имеет приятную функцию, что событие Tick
возникает в потоке пользовательского интерфейса.
Теперь, как этот, так и этот вопрос связаны с задачей, которая фактически выполняется в фоновом потоке, так что поведение, связанное с пользовательским интерфейсом этого класса таймера, не особенно полезно в этих сценариях. Код здесь реализован в соответствии с парадигмой "запустить фоновую задачу", но его можно легко изменить, так что делегат taskAction
просто вызывается непосредственно, а не запускается в Task
и ждет. Самое приятное в использовании async
/await
, в дополнение к структурному преимуществу, отмеченному выше, заключается в том, что он сохраняет зависящее от потока поведение, которое желательно из класса System.Windows.Forms.Timer
.
Ответ 13
Это должно делать то, что вы хотите. Он выполняет поток, а затем присоединяет поток до его завершения. Переходит в цикл таймера, чтобы убедиться, что он не выполняет поток преждевременно, затем снова выключается и выполняет.
using System.Threading;
public class MyThread
{
public void ThreadFunc()
{
// do nothing apart from sleep a bit
System.Console.WriteLine("In Timer Function!");
Thread.Sleep(new TimeSpan(0, 0, 5));
}
};
class Program
{
static void Main(string[] args)
{
bool bExit = false;
DateTime tmeLastExecuted;
// while we don't have a condition to exit the thread loop
while (!bExit)
{
// create a new instance of our thread class and ThreadStart paramter
MyThread myThreadClass = new MyThread();
Thread newThread = new Thread(new ThreadStart(myThreadClass.ThreadFunc));
// just as well join the thread until it exits
tmeLastExecuted = DateTime.Now; // update timing flag
newThread.Start();
newThread.Join();
// when we are in the timing threshold to execute a new thread, we can exit
// this loop
System.Console.WriteLine("Sleeping for a bit!");
// only allowed to execute a thread every 10 seconds minimum
while (DateTime.Now - tmeLastExecuted < new TimeSpan(0, 0, 10));
{
Thread.Sleep(100); // sleep to make sure program has no tight loops
}
System.Console.WriteLine("Ok, going in for another thread creation!");
}
}
}
Должно возникнуть нечто вроде:
В функции таймера!
Спать немного!
Хорошо, зайдем для создания новых потоков!
В функции таймера!
Спать немного!
Хорошо, зайдем для создания новых потоков!
В функции таймера!
...
...
Надеюсь, это поможет!
SR
Ответ 14
Отбросы этого метода ExecuteTaskCallback
. Этот бит поручается выполнять какую-то работу, но только если это еще не сделано. Для этого я использовал ManualResetEvent
(canExecute
), который изначально устанавливается для сигнала в методе StartTaskCallbacks
.
Обратите внимание, как я использую canExecute.WaitOne(0)
. Нуль означает, что WaitOne
немедленно вернется с состоянием WaitHandle
(MSDN). Если нуль опущен, вы получите все вызовы ExecuteTaskCallback
в конечном итоге, выполнив задачу, что может быть довольно катастрофическим.
Другая важная вещь - иметь возможность закончить обработку чисто. Я решил предотвратить Timer
выполнение каких-либо дальнейших методов в StopTaskCallbacks
, потому что кажется предпочтительным сделать это, пока другая работа может продолжаться. Это гарантирует, что и никакая новая работа не будет предпринята, и что последующий вызов canExecute.WaitOne();
действительно будет охватывать последнюю задачу, если она есть.
private static void ExecuteTaskCallback(object state)
{
ManualResetEvent canExecute = (ManualResetEvent)state;
if (canExecute.WaitOne(0))
{
canExecute.Reset();
Console.WriteLine("Doing some work...");
//Simulate doing work.
Thread.Sleep(3000);
Console.WriteLine("...work completed");
canExecute.Set();
}
else
{
Console.WriteLine("Returning as method is already running");
}
}
private static void StartTaskCallbacks()
{
ManualResetEvent canExecute = new ManualResetEvent(true),
stopRunning = new ManualResetEvent(false);
int interval = 1000;
//Periodic invocations. Begins immediately.
Timer timer = new Timer(ExecuteTaskCallback, canExecute, 0, interval);
//Simulate being stopped.
Timer stopTimer = new Timer(StopTaskCallbacks, new object[]
{
canExecute, stopRunning, timer
}, 10000, Timeout.Infinite);
stopRunning.WaitOne();
//Clean up.
timer.Dispose();
stopTimer.Dispose();
}
private static void StopTaskCallbacks(object state)
{
object[] stateArray = (object[])state;
ManualResetEvent canExecute = (ManualResetEvent)stateArray[0];
ManualResetEvent stopRunning = (ManualResetEvent)stateArray[1];
Timer timer = (Timer)stateArray[2];
//Stop the periodic invocations.
timer.Change(Timeout.Infinite, Timeout.Infinite);
Console.WriteLine("Waiting for existing work to complete");
canExecute.WaitOne();
stopRunning.Set();
}
Ответ 15
У меня была такая же проблема некоторое время назад, и все, что я сделал, это использовать lock {}. При этом, даже если таймер хочет что-либо сделать, он вынужден ждать до конца блокировки-блока.
то есть.
lock
{
// this code will never be interrupted or started again until it has finished
}
Это отличный способ убедиться, что ваш процесс будет работать до конца без прерывания.
Ответ 16
Я рекомендую использовать Timer вместо thread, так как это более легкий объект. Чтобы достичь своей цели, вы можете сделать следующее.
using System.Timers;
namespace sample_code_1
{
public class ClassName
{
Timer myTimer;
static volatile bool isRunning;
public OnboardingTaskService()
{
myTimer= new Timer();
myTimer.Interval = 60000;
myTimer.Elapsed += myTimer_Elapsed;
myTimer.Start();
}
private void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (isRunning) return;
isRunning = true;
try
{
//Your Code....
}
catch (Exception ex)
{
//Handle Exception
}
finally { isRunning = false; }
}
}
}
Сообщите мне, если это поможет.
Ответ 17
Вот что я сделал и, похоже, работает!
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Timers;
namespace Exercice_1_1
{
class Program
{
static void Main(string[] args)
{
Console.SetWindowPosition(0, 0);
Service backgroundWork = new Service(1, 1000);
do
{
}
while (!(backgroundWork.ExecutionCNT == 100));
}
}
class Service
{
private BackgroundWorker worker;
private int executionCNT = 0;
public int ExecutionCNT
{
get { return executionCNT; }
}
//Constructeur (Compteur du nombre d'execution, Delais entre chaque executions)
public Service(int executionCNT, int executionDelay)
{
this.executionCNT = executionCNT;
worker = new BackgroundWorker();
System.Timers.Timer timer = new System.Timers.Timer(executionDelay);
worker.DoWork += worker_DoWork;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
Console.SetCursorPosition(0, 0);
Console.WriteLine("({0})task started....\n\n", executionCNT++);
Console.WriteLine("Press any key to end it! OR ctrl + c to end exemple");
Console.ReadLine();
Console.Clear();
}
}
}