C# run a thread every X minutes, but only if that

2019-01-16 05:09发布

I have a C# program that needs to dispatch a thread every X minutes, but only if the previously dispatched thread (from X minutes) ago is not currently still running.

A plain old Timer alone will not work (because it dispatches an event every X minutes regardless or whether or not the previously dispatched process has finished yet).

The process that's going to get dispatched varies wildly in the time it takes to perform it's task - sometimes it might take a second, sometimes it might take several hours. I don't want to start the process again if it's still processing from the last time it was started.

Can anyone provide some working C# sample code?

标签: c# timer
16条回答
混吃等死
2楼-- · 2019-01-16 05:57

You can use System.Threading.Timer. Trick is to set the initial time only. Initial time is set again when previous interval is finished or when job is finished (this will happen when job is taking longer then the interval). Here is the sample code.

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);
    }
查看更多
男人必须洒脱
3楼-- · 2019-01-16 05:57

Why not use a timer with Monitor.TryEnter()? If OnTimerElapsed() is called again before the previous thread finishes, it will just be discarded and another attempt won't happen again until the timer fires again.

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);
        }
    }
查看更多
神经病院院长
4楼-- · 2019-01-16 06:01

You can disable and enable your timer in its elapsed callback.

public void TimerElapsed(object sender, EventArgs e)
{
  _timer.Stop();

  //Do Work

  _timer.Start();
}
查看更多
forever°为你锁心
5楼-- · 2019-01-16 06:01

You can just use the System.Threading.Timer and just set the Timeout to Infinite before you process your data/method, then when it completes restart the Timer ready for the next call.

    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;
    }
查看更多
相关推荐>>
6楼-- · 2019-01-16 06:01

If I understand you correctly, you actually just want to ensure your thread is not running before you dispatch another thread. Let's say you have a thread defined in your class like so.

private System.Threading.Thread myThread;

You can do:

//inside some executed method
System.Threading.Timer t = new System.Threading.Timer(timerCallBackMethod, null, 0, 5000);

then add the callBack like so

private void timerCallBackMethod(object state)
{
     if(myThread.ThreadState == System.Threading.ThreadState.Stopped || myThread.ThreadState == System.Threading.ThreadState.Unstarted)
     {
        //dispatch new thread
     }
}
查看更多
放荡不羁爱自由
7楼-- · 2019-01-16 06:03

Using the PeriodicTaskFactory from my post here

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);

The output below shows that even though the interval is set 1000 milliseconds, each iteration doesn't start until the work of the task action is complete. This is accomplished using the synchronous: true optional parameter.

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

If you want the "skipped event" behavior with the PeriodicTaskFactory simply don't use the synchronous option and implement the Monitor.TryEnter like what Bob did here https://stackoverflow.com/a/18665948/222434

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);

The nice thing about the PeriodicTaskFactory is that a Task is returned that can be used with all the TPL API, e.g. Task.Wait, continuations, etc.

查看更多
登录 后发表回答