1

I launch a timer with a callback function. But in this callback function I change/initialize a static object which is used after the launch of timer.

public class TimerExecute
{
    // Assume that the "Dog" class exist with attribute Name initialized in the constructor
    public static List<Dog> listDog = new List<Dog>();

    public void callbackFunct(String param) {

        // code...

        listDog.Add(new Dog("Bob"));

        // code...
    }

    public void Main() {
        // add dogs Bob each 10sec
        Timer addbobs = new Timer((e) => callbackFunct("arg"), null, 0, 10000);

        // return argumentoutofrange exception
        Console.WriteLine(listDog[0].name);
    }
}

When I use the static var, I have an Exception “argument out of range exception”. I think the problem is that callback function doesn’t finished her execution and the object is not yet initialize.

I tried this solution but this doesn't work :

// add dogs Bob each 10sec
Timer addbobs = new Timer((e) => callbackFunct("arg"), null, 0, 10000);
WaitHandle h = new AutoResetEvent(false);
addbobs.Dispose(h);
Console.WriteLine(listDog[0].name);

But with this, it works :

Timer addbobs = new Timer((e) => callbackFunct("arg"), null, 0, 10000);
Thread.Sleep(2000);
Console.WriteLine(listDog[0].name);

I want that my callback function finishes her execution before the next statement. Do you have a solution for my problem ?

Last Edit : Yes I want to be able to pass parameters to callbackFunct

Neyoh
  • 623
  • 1
  • 11
  • 33
  • 1
    You shouldn't get a null reference exception, you should probably get an index out of range exception. Can you give us the exact wording of the exception? – Yacoub Massad Jun 27 '16 at 18:09
  • Yes excuse me, it's an "argument out of range exception" – Neyoh Jun 27 '16 at 18:18
  • `new Timer(callbackFunct, null, 0, 10000);` doesn't compile. Can you post the code you are actually using, or fix up your example code so it compiles? – Quantic Jun 27 '16 at 18:23
  • Do you want to wait for only the first item to be added? – Yacoub Massad Jun 27 '16 at 18:25
  • @Quantic sorry i changed – Neyoh Jun 27 '16 at 18:30
  • Not submitting an answer because I don't fully understand `AutoResetEvent`'s and how to use them, but it appears your issue is that you aren't waiting long enough for the first timer callback to complete. You could use the `AutoResetEvent` somehow to know for sure that it has completed, but you are not using it correctly. To use the `AutoResetEvent` you need to [pass it into your `Timer`](https://msdn.microsoft.com/en-us/library/system.threading.timercallback(v=vs.110).aspx), which you are not doing; you pass in `null` where the AutoResetEvent is supposed to go. – Quantic Jun 27 '16 at 18:51
  • @Quantic My test with AutoResetEvent is about this response http://stackoverflow.com/a/9062414/4517421 . I will try with passing AutoResetEvent. – Neyoh Jun 27 '16 at 18:55
  • Do you need to be able to pass in parameters to your callback function? If so then I think the only way to use the `AutoResetEvent` WaitHandle is to create your own data structure that has your parameters in it as well as the WaitHandle, then your call back method has to call `autoResetEvent.Set()` which is what signals the `Timer` that the callback has "completed". If you do that, then you can pass in the custom datstructure with your WaitHandle in it, then call `handle.WaitOne()`, then after that code you can safely call `listDog[0].name`. – Quantic Jun 27 '16 at 19:28

2 Answers2

1

Here is what I came up with. The trick is to pass in the AutoResetEvent, and you have to call Set() on that event yourself which is what signals that the method is "completed" (really it just signals that the method was called whether the method is done or not). Because it appears you need other parameters sent to the call back in addition to the WaitHandle, I made a class to encapsulate both.

        public void callbackFunct(object state)
        {
            var myParams = (CustomParametersWithWaitHandle)state;
            string name = myParams.Parameter1;
            AutoResetEvent wh = myParams.WaitHandle;
            // code...

            listDog.Add(new Dog(name));

            // code...

            wh.Set(); // signal that this callback is done
        }

        public void Main()
        {
            // add dogs Bob each 10sec
            AutoResetEvent wh = new AutoResetEvent(false);
            var myCustomParams = new CustomParametersWithWaitHandle(wh, "bob", 314);
            Timer addbobs = new Timer(new TimerCallback(callbackFunct), myCustomParams, 0, 10000);
            wh.WaitOne(); // blocks here until `Set()` is called on the AutoResetEvent

            Console.WriteLine(listDog[0].name);
        }
    }

    public class CustomParametersWithWaitHandle
    {
        public AutoResetEvent WaitHandle { get; set; }
        public string Parameter1 { get; set; }
        public int Parameter2 { get; set; }

        public CustomParametersWithWaitHandle(AutoResetEvent h, string parameter1, int parameter2)
        {
            WaitHandle = h;
            Parameter1 = parameter1;
            Parameter2 = parameter2;
        }
Quantic
  • 1,779
  • 19
  • 30
  • Oh nice trick, I will prefer pass my parameters directly with callback function as i did. But i will try this, I keep you in the loop – Neyoh Jun 28 '16 at 09:35
  • I just learned about this stuff so it may be wrong. But from what I understand, the example you are following means you *have* to use the callback function for the `AutoResetEvent`, and the parameters of the callback must accept the event so that it can call `event.Set()` on it, so you can't just pass in a `string` as the parameter to your callback function because it must take in the event signature of `object` and that object has the `AutoResetEvent` in it. So with my custom class it now has your own parameters too. – Quantic Jun 28 '16 at 14:35
  • 1
    And there is no need to create a TimerCallBack just pass callbackFunct `new Timer(callbackFunct, myCustomParams, 0, 10000);` – Neyoh Jun 29 '16 at 06:12
0

I'm quite sure you should be initializing your TimerCallback with new TimerCallback(callbackFunct) instead of only the name of the function. That should be the reason your list is not being filled with Bobs (I can't seem to understand how it even compiles but...). Like:

Timer addbobs = new Timer(new TimerCallback(callbackFunct), null, 0, 10000);

The your function shall look like this:

 public void callbackFunct(object state){
      //...
      listDog.Add(new Dog("Bob"));
      //...
 }

It might be possible to initialize it without a new instance, but I'm not quite sure... P.S.: I suspect that's not the code you're using, since it doesn't even compile. Take care to update it...

D. Petrov
  • 1,147
  • 15
  • 27
  • "That should be the reason your list is not being filled with Bobs" -- maybe, maybe not. His code doesn't even compile, so it's hard to say if your compilation fix actually fixes his problem because he must be running different code that *does* compile. – Quantic Jun 27 '16 at 18:22
  • I guess so, but as I see his edit now, I begin to think that his function is ok and the problem is somewhere else, since it does fill the bobs after a delay... – D. Petrov Jun 27 '16 at 18:23