Несколько io.filesystemwatchers параллельно - PullRequest
2 голосов
/ 09 февраля 2020

У меня есть три разные задачи, которые я sh должен передать сторонним наблюдателям файловой системы в powershell. У меня есть код, настроенный для инициализации двух наблюдателей и проверки каждые десять секунд, чтобы убедиться, что они работают. Однако задачи, которые они выполняют, длятся менее минуты и 5 минут соответственно. Третья задача, которую я sh должен передать стороннему наблюдателю, занимает около часа. Я обеспокоен тем, что, если все они будут запущены одновременно, задачи, за которыми должны наблюдать первые два, вообще не будут выполнены, если третий наблюдатель выполняет свое действие изменения. Есть ли способ реализовать или запустить их так, чтобы действия по изменению могли выполняться параллельно?

1 Ответ

1 голос
/ 09 февраля 2020

Командлет Start-ThreadJob можно использовать для параллельного запуска задач просмотра файлов.

  • Start-ThreadJob поставляется с модулем ThreadJob и предлагает облегченную альтернативу thread на основе обычных фоновых заданий на основе дочерних процессов. Он поставляется с PowerShell [Core] v6 + и в Windows PowerShell может быть установлен по требованию, например, Install-Module ThreadJob -Scope CurrentUser. В большинстве случаев задания потоков являются лучшим выбором как по производительности, так и по точности воспроизведения - см. Нижний раздел этот ответ , почему.

Следующий автономный пример кода :

  • использует задания потоков для параллельного запуска двух отдельных задач мониторинга и обработки файлов,
  • , которые не блокируют ни друг друга, ни вызывающую сторону.

Примечание:

  • Каждая задача создает свой собственный экземпляр System.IO.FileSystemWatcher в приведенном ниже коде, хотя создание слишком большого числа из них может создать значительную нагрузку на систему, возможно, в результате чего события пропускаются. Альтернативой является доля экземпляров, например, создание одного экземпляра в контексте вызывающего, к которому могут обращаться задания потока (см. Комментарии в исходном коде ниже).

  • [ Это отчасти умозрительно; сообщите нам, если я что-то не так понял ] Direct FileSystemWatcher. NET Делегаты-обработчики событий должны быть короткими, но подписка на события из PowerShell с помощью задания событий, созданного Register-ObjectEvent ставит в очередь события на стороне PowerShell, которые PowerShell затем отправляет в блоки сценариев -Action, так что эти блоки для выполнения длительных операций ниже не должны быть Непосредственная * проблема 1052 * (хотя задачи могут занять много времени).

# Make sure that the ThreadJob module is available.
# In Windows PowerShell, it must be installed first.
# In PowerShell [Core], it is available by default.
Import-Module ThreadJob -ea Stop

try {

  # Use the system's temp folder in this example.
  $dir = (Get-Item -EA Ignore temp:).FullName; if (-not $dir) { $dir = $env:TEMP }

  # Define the tasks as an array of custom objects that specify the dir.
  # and file name pattern to monitor as well as the action script block to 
  # handle the events.
  $tasks = # array of custom objects to describe the 
    [pscustomobject] @{
      DirToMonitor = $dir
      FileNamePattern = '*.tmp1'
      Action = {
        # Print status info containing the event data to the host, synchronously.
        Write-Host -NoNewLine "`nINFO: Event 1 raised:`n$($EventArgs | Format-List | Out-String)"
        # Sleep to simulate blocking the thread with a long-running  task.
        Write-Host "INFO: Event 1: Working for 4 secs."
        Start-Sleep 4
        # Create output, which Receive-Job can collect.
        "`nEvent 1 output: " + $EventArgs.Name
      }
    },
    [pscustomobject] @{
      DirToMonitor = $dir
      FileNamePattern = '*.tmp2'
      Action = {
        # Print status info containing the event data to the host, synchronously
        Write-Host -NoNewLine "`nINFO: Event 2 raised:`n$($EventArgs | Format-List | Out-String)"
        # Sleep to simulate blocking the thread with a long-running  task.
        Write-Host "INFO: Event 2: Working for 2 secs"
        Start-Sleep 2
        # Create output, which Receive-Job can collect.
        "`nEvent 2 output: " + $EventArgs.Name
      }  
    } 

  # Start a separate thread job for each action task.
  $threadJobs = $tasks | ForEach-Object {

    Start-ThreadJob -ArgumentList $_ {

      param([pscustomobject] $task)

      # Create and initialize a thread-specific watcher.
      # Note: To keep system load low, it's generally better to use a *shared* 
      #       watcher, if feasible. You can define it in the caller's scope
      #       and access here via $using:watcher
      $watcher = [System.IO.FileSystemWatcher] [ordered] @{
        Path   = $task.DirToMonitor
        Filter = $task.FileNamePattern
        EnableRaisingEvents = $true # start watching.
      }

      # Subscribe to the watcher's Created events, which returns an event job.
      # This indefinitely running job receives the output from the -Action script
      # block whenever the latter is called after an event fires.
      $eventJob = Register-ObjectEvent -ea stop $watcher Created -Action $task.Action

      Write-Host "`nINFO: Watching $($task.DirToMonitor) for creation of $($task.FileNamePattern) files..."

      # Indefinitely wait for output from the action blocks and relay it.
      try {
        while ($true) {
          Receive-Job $eventJob
          Start-Sleep -Milliseconds 500  # sleep a little
        }
      }
      finally { 
         # !! This doesn't print, presumably because this is killed by the
         # !! *caller* being killed, which then doesn't relay the output anymore.
        Write-Host "Cleaning up thread for task $($task.FileNamePattern)..."
        # Dispose of the watcher.
        $watcher.Dispose()
        # Remove the event job (and with it the event subscription).
        $eventJob | Remove-Job -Force 
      }

    }

  }  

  $sampleFilesCreated = $false
  $sampleFiles = foreach ($task in $tasks) { Join-Path $task.DirToMonitor ("tmp_$PID" + ($task.FileNamePattern -replace '\*')) }

  Write-Host "Starting tasks...`nUse Ctrl-C to stop."

  # Indefinitely wait for and display output from the thread jobs.
  # Use Ctrl+C to stop.
  $dtStart = [datetime]::UtcNow
  while ($true) {

    # Receive thread job output, if any.
    $threadJobs | Receive-Job

    # Sleep a little.
    Write-Host . -NoNewline
    Start-Sleep -Milliseconds 500

    # A good while after startup, create sample files that trigger all tasks.
    # NOTE: The delay must be long enough for the task event handlers to already be
    #       in place. How long that takes can vary.
    #       Watch the status output to make sure the files are created
    #       *after* the event handlers became active.
    #       If not, increase the delay or create files manually once
    #       the event handlers are in place.
    if (-not $sampleFilesCreated -and ([datetime]::UtcNow - $dtStart).TotalSeconds -ge 10) {
      Write-Host
      foreach ($sampleFile in $sampleFiles) {
        Write-Host "INFO: Creating sample file $sampleFile..."
        $null > $sampleFile
      }
      $sampleFilesCreated = $true
    }

  }

}
finally {
  # Clean up.
  # Clean up the thread jobs.
  Remove-Job -Force $threadJobs
  # Remove the temp. sample files
  Remove-Item -ea Ignore $sampleFiles
}

Приведенное выше создает вывод, такой как следующий (пример с компьютера MacOS ):

Starting tasks...
Use Ctrl-C to stop.
.
INFO: Watching /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/ for creation of *.tmp1 files...

INFO: Watching /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/ for creation of *.tmp2 files...
.........
INFO: Creating sample file /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/tmp_91418.tmp1...
INFO: Creating sample file /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/tmp_91418.tmp2...
.
INFO: Event 1 raised:

ChangeType : Created
FullPath   : /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/tmp_91418.tmp1
Name       : tmp_91418.tmp1


INFO: Event 1: Working for 4 secs.

INFO: Event 2 raised:

ChangeType : Created
FullPath   : /var/folders/19/0lxcl7hd63d6fqd813glqppc0000gn/T/tmp_91418.tmp2
Name       : tmp_91418.tmp2


INFO: Event 2: Working for 2 secs
....
Event 2 output: tmp_91418.tmp2
....
Event 1 output: tmp_91418.tmp1
.................
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...