同步使用 Add-Content 的 PowerShell 后台作业
Synchronising PowerShell background jobs that use Add-Content
我有一个小型 PowerShell 程序,它启动几个线程来进行并行计算,然后在它们完成后将包含结果的一行附加到文本文件中,然后继续执行更多操作。这在开发和测试中运行良好,但在生产中偶尔会挂起,而且文件似乎是 "jammed open"。我将写入包裹在 "try" 块中,但这无济于事。我写了一个玩具应用程序来说明这个问题,它通常在大约 10-15 分钟后挂起(写了大约 3000 行)。
在我看来,如果 Python 解决方案使用互斥锁或其他东西,我会过得更好,但我现在在这条路上走得很远。寻找想法如何轻松解决此问题。我真的以为 Add-Content 会是原子的...
Parentjob.ps1
# Start a bunch of jobs
$curdir = "c:\transfer\filecollide"
$tokens = "tok00","tok01","tok02",
"tok03","tok04","tok05",
"tok06","tok07","tok08"
$jobs = @()
foreach ($tok in $tokens)
{
$job = Start-Job -FilePath ".\childjob.ps1" -ArgumentList "${curdir}",$tok,2,1000
Start-Sleep -s 3 # stagger things a bit
Write-Output " Starting:${tok} job"
$jobs += ,$job
}
foreach ($job in $jobs)
{
wait-job $job
$out = receive-job $job
Write-Output($out)
}
childjob.ps1
param(
[string]$curdir = ".",
[string]$tok = "tok?",
[int]$interval = 10,
[int]$ntodo = 1
)
$nwritefails = 0
$nwritesuccess = 0
$nwrite2fails = 0
function singleLine
{
param(
[string]$tok,
[string]$fileappendout = "",
[int]$timeout = 3
)
$curdatetime = (Get-Date)
$sout = "${curdatetime},${tok},${global:nwritesuccess},${global:nwritefails},${global:nwrite2fails}"
$global:nwritesuccess++
try
{
Add-Content -Path $fileappendout -Value "${sout}"
}
catch
{
$global:nwritefails++
try
{
Start-Sleep -s 1
Add-Content -Path $fileappendout -Value "${sout}"
}
catch
{
$global:nwrite2fails++
Write-Output "Failed to write to ${fileappendout}"
}
}
}
Write-Output "Starting to process ${tok}"
#Start of main code
cd "${curdir}"
$ndone = 0
while ($true)
{
singleLine $tok "outfile.txt"
$ndone++
if ($ndone -gt $ntodo){ break }
Start-Sleep -s $interval
}
Write-Output "Successful ${tok} appends:${nwritesuccess} failed:${nwritefails} failed2:${nwrite2fails}"
为什么不让作业将结果写入输出流,并在主线程中使用 Receive-Job 来收集结果并更新文件?您可以在作业仍然 运行 时执行此操作。您写入输出流的内容现在看起来可能更适合写入 Progress 流。
我有一个小型 PowerShell 程序,它启动几个线程来进行并行计算,然后在它们完成后将包含结果的一行附加到文本文件中,然后继续执行更多操作。这在开发和测试中运行良好,但在生产中偶尔会挂起,而且文件似乎是 "jammed open"。我将写入包裹在 "try" 块中,但这无济于事。我写了一个玩具应用程序来说明这个问题,它通常在大约 10-15 分钟后挂起(写了大约 3000 行)。
在我看来,如果 Python 解决方案使用互斥锁或其他东西,我会过得更好,但我现在在这条路上走得很远。寻找想法如何轻松解决此问题。我真的以为 Add-Content 会是原子的...
Parentjob.ps1
# Start a bunch of jobs
$curdir = "c:\transfer\filecollide"
$tokens = "tok00","tok01","tok02",
"tok03","tok04","tok05",
"tok06","tok07","tok08"
$jobs = @()
foreach ($tok in $tokens)
{
$job = Start-Job -FilePath ".\childjob.ps1" -ArgumentList "${curdir}",$tok,2,1000
Start-Sleep -s 3 # stagger things a bit
Write-Output " Starting:${tok} job"
$jobs += ,$job
}
foreach ($job in $jobs)
{
wait-job $job
$out = receive-job $job
Write-Output($out)
}
childjob.ps1
param(
[string]$curdir = ".",
[string]$tok = "tok?",
[int]$interval = 10,
[int]$ntodo = 1
)
$nwritefails = 0
$nwritesuccess = 0
$nwrite2fails = 0
function singleLine
{
param(
[string]$tok,
[string]$fileappendout = "",
[int]$timeout = 3
)
$curdatetime = (Get-Date)
$sout = "${curdatetime},${tok},${global:nwritesuccess},${global:nwritefails},${global:nwrite2fails}"
$global:nwritesuccess++
try
{
Add-Content -Path $fileappendout -Value "${sout}"
}
catch
{
$global:nwritefails++
try
{
Start-Sleep -s 1
Add-Content -Path $fileappendout -Value "${sout}"
}
catch
{
$global:nwrite2fails++
Write-Output "Failed to write to ${fileappendout}"
}
}
}
Write-Output "Starting to process ${tok}"
#Start of main code
cd "${curdir}"
$ndone = 0
while ($true)
{
singleLine $tok "outfile.txt"
$ndone++
if ($ndone -gt $ntodo){ break }
Start-Sleep -s $interval
}
Write-Output "Successful ${tok} appends:${nwritesuccess} failed:${nwritefails} failed2:${nwrite2fails}"
为什么不让作业将结果写入输出流,并在主线程中使用 Receive-Job 来收集结果并更新文件?您可以在作业仍然 运行 时执行此操作。您写入输出流的内容现在看起来可能更适合写入 Progress 流。