views:

46

answers:

2

I am using System.Threading.Timer in my Windows service and locking the callback method using Monitor.TryEnter, so it's non-reentrant. Inside the callback, I am looping over some database objects (Linq to SQL entities) and performing some IO tasks. On each iteration of the loop, I am changing some properties of entity to flag it as processed. After the loop exits, I call SubmitChanges on the datacontext, which persists the changes to the database. The following problem arises: if the service is stopped while the callback is executing, some of the IO tasks may have already been performed, but the records have not been flagged as processed in the database (i.e. SubmitChanges has not been called yet) -- clearly, not what I want to happen. Somehow, I need to communicate to the callback worker thread that the OnStop event has fired to allow it to submit changes and wrap things up. How best to do this?

A: 

You could look at using the Task Parallel Library. Have a read of the Task Cancellation page. This would give you a way to create worker threads that can tidy up cleanly in response to a cancellation, if I've understood your needs correctly.

SamStephens
Looks very interesting, but unfortunately I am still using Framework 3.5 :(
Antony Highsky
+1  A: 

1st decide if you will finish the tasks that callback performs or you will rollback them. So if you decide to finish the tasks, you will perform the callback to the end. Time should be canceled in OnStop already. If you will go with the second option (rollback) your code will look something like that:

bool shouldAbort=false;

TimerProc()
{
     Step1();
     if (shouldAbort)
     {
         UndoStep1();
         return;
     }
     Step2();
     if (shouldAbort)
     {
         UndoStep2();
         UndoStep1();  //  or vice versa, depending on your operations
         return;
     }
     // ...
}

in OnStop()

timer.Stop();  //  don't worry here - your TimerProc() WILL finish
shouldAbort=true;
Daniel Mošmondor
if you have worries about TimerProc() not completing, use AutoResetEvent and WaitOne() on it after killing the timer, and Set() the event at the end of the TimerProc()
Daniel Mošmondor
Thanks for the suggestion. I *am* stopping the timer in OnStop: timer.Change(Timeout.Infinite, 0) -- am using the Timer from the Threading namespace. But I also want to signal to the callback thread to wrap up quickly (no rollback). Perhaps a scenario that makes the need more apparent is a shutdown event.
Antony Highsky
There are two Antonys: one who wants the task to wrap up quickly, and other that doesn't want long i/o tasks to be forcibly killed. One should give up, let's give them some time to decide :)
Daniel Mošmondor
Hehe.. maybe. It's really a sequence of several distinct IO processes: create a file, FTP a file, send some emails, perform some db tasks, etc. Each task has a completion flag associated with it in the db. Both Antonys want to interrupt the sequence, not an individual IO operation. Hope this clears things up.
Antony Highsky
So, hm, where's the problem. Raise some flag as suggested, exit, and that's it? Hm, am I missing something here?
Daniel Mošmondor
I can set some variables at the class level and check them before performing each task. Do you see any issues with that since I am accessing these variables from a background thread? I've asked the question b/c multithreading is not my strength -- I come from a web dev background. Thanks again.
Antony Highsky
No, there will be no issues with the variables, they are shared between threads.
Daniel Mošmondor