0

This question is possibly a duplicate of How do you add a timer to a C# console application and few other similar questions but couldn't find the answer I'm looking for so asking again.

Question: How do you pass data from the Elapsed event of a Timer (System.Timers.Timer) to the thread that created the timer (that thread may not be the Main thread but another thread spawned by Main)?

I assume there could be some trivial way of achieving this eg. like the BackgroundWorker ProgressChanged event being called in the thread that created the worker, but couldn't find a way in MSDN documentation or SO. Most examples I've seen do some action in the timer thread (https://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx) but don't pass anything to the original thread. Needless to say I'm pretty new to C#/.NET so a solution + pointers to references are appreciated.

Edit: I'd prefer not to use the Invoke()/InvokeRequired pattern (cf. How to use safe threading for a timer(Change timer properties from different thread) ) as this is not for a Forms application. I'm tempted to solve this by creating a BackgroundWorker that reports to the original thread at intervals (DoWork will be just a sleep() inside a loop) but thought .NET might have this already and hence the question.

Community
  • 1
  • 1
ubi
  • 4,041
  • 3
  • 33
  • 50

3 Answers3

0

Will you consider use a dispatcher? (although you need invoke some method too)

When some thread (maybe not the main thread) create the timer which you mentioned, you can create the dispatcher with the same thread too. After that, dispatcher.invoke() will let the original thread to do those task for you. See the MSDN for more information.

J.C
  • 633
  • 1
  • 13
  • 27
  • Dispatcher is from `System.Windows.Threading.Dispatcher` and MSDN says it's for WPF. I'm bit hesitant to use anything WPF in this case as what I'm working on is meant to be re-usable for both Console and Windows applications. Correct me if that's a false concern (ie. it is OK to use WPF classes for Console applications too) – ubi Jul 29 '15 at 03:59
  • Don't worry, dispatcher works in winform too. Please refer to http://stackoverflow.com/questions/303116/system-windows-threading-dispatcher-and-winforms BTW, I have used it in my winform project, too. – J.C Jul 29 '15 at 04:02
0

It seems to me that a simple locking mechanism is what you need:

private Object _sync = new Object();

void MyTimerFinished() 
{
    lock (_sync)
    {
        // Access shared data
        ...
    }
}

void CodeExecutingInMainThread() 
{
    lock (_sync)
    {
        // Access shared data
        ...
    }   
}
Paul Carroll
  • 1,523
  • 13
  • 15
  • 1
    Combine the locking mechanism with a [`Queue`](https://msdn.microsoft.com/en-us/library/7977ey2c(v=vs.110).aspx) and you have a complete solution – alldayremix Jul 29 '15 at 04:43
0

Ok, so this is what I came up with (a solution with locks and Queues sounds a bit too complex for me - may be simpler but haven't tried)

public class ReportingTimer
{
    public event EventHandler Elapsed;
    private int _interval;
    private BackgroundWorker _worker;

    public ReportingTimer(int interval)
    {
        _interval = interval;
        _worker = new BackgroundWorker();
        _worker.WorkerReportsProgress = true;
        _worker.WorkerSupportsCancellation = true;

        _worker.DoWork += _worker_DoWork;
        _worker.ProgressChanged += _worker_ProgressChanged;

    }

    public void Start()
    {
        if (!_worker.IsBusy)
        {
            _worker.RunWorkerAsync();
        }
    }

    public void Stop()
    {
        if (_worker.IsBusy)
        {
            _worker.CancelAsync();
        }
    }

    private void _worker_DoWork(object sender, DoWorkEventArgs e)
    {

        while (!_worker.CancellationPending)
        {
            Thread.Sleep(_interval);
            _worker.ReportProgress(1);
        }

        if (_worker.CancellationPending)
        {
            e.Cancel = true;
        }
    }

    private void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (!_worker.CancellationPending)
        {
            if (Elapsed != null)
            { 
                Elapsed(this, new EventArgs());
            } 

        }
    }
}

Please critique.

Edit: This serves my purpose and the interface is similar to the Timer class and is actually what I wanted from the stock Timer.

ubi
  • 4,041
  • 3
  • 33
  • 50