views:

1073

answers:

3

The documentation of System.Threading.Timer says that I should keep a live reference for it to avoid it being garbage collected. But where should I do that? My main is very simple that I don't know where to keep the reference:

class Program {
    static void Main() {
        new System.Threading.Thread(myThreadStart).Start();
        new System.Threading.Timer(myTimerCallback, new MyStateObject(), 0, 5000);
    }
}

I thought about keeping the reference in a static field in the Program class, assuming that static fields do not get collected until the end of the application. But I'm not sure this is the best way to do it, so I'd appreciate your advice.

+4  A: 

EDIT: My original answer is rubbish. Really rubbish. I've kept it here to explain why it's rubbish though - it's in the comments, but they'd have been deleted with the answer.

GC.KeepAlive only makes sure a reference is treated as a root until after the call. In the code at the bottom of this answer, the GC.KeepAlive method would be called immediately, and then the timer would still be eligible for garbage collection. Because the newly created thread is a foreground thread, the app will run as long as it's alive (whereas the timer uses a background thread, which doesn't prevent program exit). This means that the Main method exits, but the application needs to keep running.

Arguably a simpler solution would be to run myThreadStart in the main thread, rather than creating a new one and then letting the main thread die. In other words, a simple solution would be:

using System.Threading;

class Program {
    static void Main() {
        Timer timer = new Timer(myTimerCallback, 
                                new MyStateObject(), 0, 5000);
        myThreadStart();
        GC.KeepAlive(timer);
    }
}

I assume the real code is more complicated though - in which case using a private static variable as suggested in other answers is probably the way to go. It really will depend on the usage though. I personally prefer not to create a static field just for the sake of preventing something being collected if there's an alternative (like the above) but sometimes it's virtually the only way of doing it.

Original (bad) answer:

If you really want to allocate it in Main, then you can use GC.KeepAlive:

using System.Threading;

class Program {
    static void Main() {
        new Thread(myThreadStart).Start();
        Timer timer = new Timer(myTimerCallback, 
                                new MyStateObject(), 0, 5000);
        GC.KeepAlive(timer);
    }
}
Jon Skeet
dang, beaten to it!
Mitch Wheat
Thanks. According to the documentation of KeepAlive, it "references the specified object, which makes it ineligible for garbage collection from the start of the current routine to the point where this method is called." Doesn't this mean that, by the end of the main method, the object can be GC'd?
Hosam Aly
@Hosam - you'll need to keep your Main() method active somehow anyway - returning from it will terminate your program.
Michael Burr
@Michael Burr, program won't exit whild thread created via (new Tread...) is running.
aku
@Michael, yes you're right, I noticed that after I wrote the question. @aku, you're right too, I had just tried it. Thanks everybody.
Hosam Aly
@Hosam Aly, now you need to click on "accept" button near the correct answer :)
aku
@aku, I was waiting for a while before doing that, as per an advice in the FAQs, so as to give other people some time to answer. The FAQ suggested waiting for one day. What do you think?
Hosam Aly
@Hosam Aly, I think I should read FAQ :)
aku
Editing this answer...
Jon Skeet
Okay, answer now makes sense, hopefully.
Jon Skeet
"I personally prefer not to create a static field just for the sake of preventing something being collected if there's an alternative (like the above) ". I disagree - a static reference is the obvious way to keep an object from being garbage collected. KISS.
Joe
@Joe, +1, I think code is much more simple when using static field. It's like saying "I want this stuff to live as long as the parent class".
aku
@Joe: It's an obvious way of doing it, but the timer isn't obvious state for the type in my view. It's certainly a matter of persona taste though.
Jon Skeet
@aku - regarding returning from Main() terminating the process even if other threads are running - you are correct. It turns out that .NET (or maybe c#) is different from a standard Win32 program in this aspect. I was not aware of that.
Michael Burr
@Michael - I think it depends on whether other threads are background threads - see http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(VS.80).aspx
Joe
+2  A: 

I think it is OK to keep is a private static field of your class.

I would keep this reference as a static field rather than playing with garbage collector.

aku
Thanks. I guess the garbage collector wouldn't collect a static field, even if it was private (and no longer accessed), would it?
Hosam Aly
it would keep it alive
aku
basically two methods lead to similar result - reference to timer won't be collected while your program is running (i.e. thread you created via new Thread.. is alive).
aku
I guess you may want to remove "GC.KeepAlive" from your answer after Jon Skeet's clarification. :)
Hosam Aly
+8  A: 

If your Timer is an application-level object there's nothing wrong with making it a private static member of your Main class. That's what I would do, anyway.

gkrogers
+1 This is the most obvious and simplest solution.
Joe