Windows サービスのワーカー スレッドに Tasks を使用するのは適切な設計でしょうか? それとも、Thread.Start() を使用したほうがよいでしょうか? ワーカーがほとんどアイドル状態で、FileSystemWatcher イベントによってトリガーされ、Take() off BlockingCollections を使用して処理を行う場合は、ワーカーを LongRunning として開始することさえできないかもしれません。
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
編集
上記のアプローチでは、Take() でブロックされるため、アイドル時にワーカー スレッドが解放されません。次のソリューションでは、BlockingCollection の代わりに ActionBlock を使用します。このソリューションは、新しいファイルをぼんやりと監視している間、スレッドを消費しません。スレッドを起動して新しいファイルを処理し、完了したら解放します。また、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
'...
クラス終了