Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to use Tasks for Windows Service workers

Would I be a good design to use Tasks for worker threads in a Windows Service, or should we better stick with Thread.Start()? Maybe not even start the workers as LongRunning if they are mostly idle, get triggered by FileSystemWatcher Events an do their processing using Take() off BlockingCollections.

Public Class FileWatcherService
Private _watchPaths As New List(Of String) From {"x:\Dir1","x:\Dir2","y:\Dir1", ...}
Private _workers As New List(Of Task)
Private _cancellationTokenSource As New CancellationTokenSource()
Private _cancellationToken As CancellationToken = _cancellationTokenSource.Token

Protected Overrides Sub OnStart(ByVal args() As String)
    For Each path In _watchPaths
        _workers.Add(
            Task.Factory.StartNew(
                Sub()
                    Dim fileProcessor As New FileProcessor
                    fileProcessor.StartWorking(path, _cancellationToken)
                End Sub, TaskCreationOptions.LongRunning, _cancellationToken))
    Next
End Sub

Protected Overrides Sub OnStop()
    _cancellationTokenSource.Cancel()
    Task.WaitAll(_workers.ToArray)
End Sub
End Class

Class FileProcessor
Private _newFiles As New BlockingCollection(Of String)
Sub _fileWatcher_Created(sender As Object, e As FileSystemEventArgs)
    _newFiles.Add(e.FullPath, _cancellationToken)
End Sub

Async Function ProcessNewFiles() As Task
    Do
        Await ProcessFile(_newFiles.Take(_cancellationToken))
    Loop
End Function
End Class

EDIT

The approach above does not release the worker threads when idle, because the block on Take(). The following solution uses an ActionBlock instead of the BlockingCollection. This solution does not consume threads while idly watching for new files. I spins up threads to process new files and releases them when done. And I no longer start the top-level worker tasks with LongRunning.

Class FileProcessor
Private _newFilesActionBlock As New ActionBlock(Of String)(
    Async Function(filePath)
        Await ProcessFile(filePath)
    End Function,
        New ExecutionDataflowBlockOptions With {
            .CancellationToken = _cancellationToken})

Sub _fileWatcher_Created(sender As Object, e As FileSystemEventArgs)
                        Handles __fileWatcher.Created
    _newFilesActionBlock.Post(e.FullPath)
End Sub
'...

End Class

like image 204
Peter Meinl Avatar asked Dec 28 '25 16:12

Peter Meinl


1 Answers

TPL is more than welcome addition to the .NET framework. It makes your threading code much easier to work with and more readable. It allows you to make your windows service (or any other threading code) multi-threaded without having to instantiate and deal with thread pools and individual threads.

I am using TPL in my windows service and it works great for me and I would most certainly recommend using TPL instead of classic thread pools in most cases.

That being said there are some very rare cases where you would still want to handle the thread pools yourself, but based on your code snippet it appears you don't really need to bother with that...

like image 156
Dean Kuga Avatar answered Dec 31 '25 18:12

Dean Kuga



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!