title: Is it OK to use Tasks for Windows Service workers
body: <p>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.</p>
  
  <pre><code>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
  </code></pre>
  
  <p>EDIT</p>
  
  <p>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.</p>
  
  <pre><code>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
  '...
  </code></pre>
  
  <p>End Class</p>