Подтвердить что ты не робот

С# запускать поток каждые X минут, но только если этот поток еще не запущен

У меня есть программа на С#, которая должна отправлять поток каждые X минут, но только если ранее отправленный поток (с X минут) назад еще не запущен.

Обычный простой Timer не будет работать (поскольку он отправляет событие каждые X минут независимо от того, закончил ли ранее обработанный процесс еще).

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

Может ли кто-нибудь предоставить примерный пример кода С#?

4b9b3361

Ответ 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();
        }
    }
}