Reputation: 52932
I've been researching this for a while but it seems things have changed over the years and there are some conflicting answers.
The way I would traditionally have done it is, in my OnStart
method,
new Thread(new ThreadStart(QueueProcessorDoWork));
So that it kicks off the thread then immediately returns.
And then:
private void QueueProcessorDoWork()
{
while (_isRunning)
{
DoStuff();
Thread.Sleep(100);
}
}
There is no way to make the DoStuff() a blocking operation, it has to basically poll.
Now, Thread.Sleep
is considered bad practice because it locks up the thread.
So research has showed me that I can use a timer. No problem with that, I would create a timer, and set it to poll every 100ms, and in the timer handler, I would disable the timer, do the processing, re-enable the timer again, and we'd be all good.
However I then read about using async/await with Task.Delay(100)
.
That sounds good. I would still need to create a Thread
, I believe, because otherwise the OnStart method wouldn't return. However this is unclear, if I call an async method without awaiting it, I don't know what would happen.
So question 1 is - should I just call an async Task that contains the infinite loop without awaiting it to start my windows service?
If I do want to use async/await within a thread, how exactly do I go about getting started? My ThreadStart can call a function that in turn does Task.Run(async () => Worker())
or something, I am not sure since the caller would need to be marked async?
So question 2 is, how would I set up the async/await pattern within a call to new Thread()
?
And the final question is, which (or both?) of the two method are the 'correct' way to start an infinite processing loop?
Upvotes: 0
Views: 889
Reputation: 456407
Now, Thread.Sleep is considered bad practice because it locks up the thread.
Yes and no. Thread.Sleep
is not ideal, but if you know this code is always running on a desktop machine, then one extra thread for the lifetime of the process is pretty cheap. I wouldn't lose sleep over it.
So research has showed me that I can use a timer.
Yes, a timer is a fine solution.
However I then read about using async/await with Task.Delay(100).
Sure, you can do this, too. Internally, Task.Delay
just starts a timer and finishes the task when the timer fires. So it's practically the same thing.
I would still need to create a Thread, I believe, because otherwise the OnStart method wouldn't return. However this is unclear, if I call an async method without awaiting it, I don't know what would happen.
As soon as an await
needs to (asynchronously) wait for an operation to complete, the async
method returns. So if you change your loop to await Task.Delay
first and then DoStuff
, a separate thread would not be necessary. Alternatively, you could just call QueueProcessorDoWork
wrapped in a Task.Run
, which will queue it to the thread pool.
So question 1 is - should I just call an async Task that contains the infinite loop without awaiting it to start my windows service?
That is one possibility:
var task = Task.Run(() => QueueProcessorDoWork());
Note that task
will complete with an exception if the loop throws an exception. I recommend having a "management" method that await
s that task and exits the service if it ever completes:
async Task MainAsync()
{
try { QueueProcessorDoWork(); }
catch (Exception ex) { /* log */ }
finally { /* stop the Win32 service */ }
}
So question 2 is, how would I set up the async/await pattern within a call to new Thread()?
You don't. Thread.Sleep
is somewhat discouraged, but new Thread
is most definitely discouraged. Unless you're using COM interop, you shouldn't create manual threads.
And the final question is, which (or both?) of the two method are the 'correct' way to start an infinite processing loop?
Just do what works for you. Either a timer or Task.Run
wrapping a loop with Task.Delay
would work fine.
Upvotes: 4