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
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With