我创造了PowerShell 脚本它将我下载的所有电子书复制到一个预先确定的目录中,我的电子书管理器会定期扫描该目录并将其添加到我的书库中。每次下载后都会立即运行该脚本。
问题是,当同时或大约同一时间下载多本书时,电子书管理器就会锁定并且没有响应。
因此,我想使用 PowerShell 作业对复制进行排队,但我不知道如何创建单个队列(单并发),以便每个后续作业将等待每个旧作业的完成。
也就是说,我希望脚本创建一个作业(我们称之为“Book Job”),该作业定期检查正在运行的 Book Job 队列,以查看在它运行之前所有较旧的 Book Job 是否已完成。当它完成时,Book Job 应该以某种方式声明它已完成,以便较新的 Book Job 可以检测到。
有人知道我该怎么做吗?我在这里看到了一个类似的问题:Powershell 后台任务但是,就我而言,我多次运行该脚本(每次新下载后)。
答案1
我的想法是通过为每个新脚本实例创建一个锁定文件来建立队列。脚本运行时,它会检查一个专门用于跟踪队列的目录,以查找脚本的现有实例。如果没有,脚本会将自己添加到队列的前面,执行一些操作(运行代码),然后清除其锁定。如果有锁定,则会在队列末尾添加一个新的锁定,实例将不断检查,直到它位于队列的前面。
这使您可以多次运行相同的脚本,所有脚本都通过检查外部可用队列单独处理。
锁文件的结构为索引、分隔符(“_”)、进程ID。
Clear-Host
function New-Lock ([int] $index) {
$newLock = "$index" + "_" + $pid + ".lck"
New-Item $queue$newLock | Out-Null
}
$queue = "C:\locks\"
# find the end of the stack
$locks = gci $queue *.lck | sort | select -expandproperty name
# if locks exist, find the end of the stack by selecting the index of the last lock
if($locks) {
# gets the last lock file, selects the index by splitting on the delimiter
[int]$last = [convert]::ToInt32(($locks | select -last 1).Split("_")[0],10)
# add the lock to the end of the stack
New-Lock ($last + 1)
}
# if no locks exist, create one at the top of the stack
else {
New-Lock 0
}
# check if we're at the top of the stack
do {
$locks = gci $queue *.lck | sort | select -expandproperty name
# this is the PID on the top of the stack
[int]$top = [convert]::ToInt32(($locks | select -first 1).Split("_")[1].Split(".")[0],10)
write-verbose "not at the top..."
sleep 1
} until ($pid -eq $top)
# if we're here, we've been to the top. it's our turn to do something
Write-Verbose "we've reached the top!"
# <do something. put your code here>
# might be good to add some Start-Sleep here
# </do something put your code here>
# now that we're done, let's delete our lock
gci $queue | select -first 1 | Remove-Item
下面是一个虚构的时间线示例,其中您下载了三个文件(我选择了随机 PID)。
- 下载文件 1 并启动脚本。没有现有锁。创建锁“0_19831”。我们位于堆栈顶部,因此您的代码已执行。这是一本很大的电子书,因此您的文件传输代码将需要整整一分钟才能运行。
- 下载文件 2 并启动脚本。锁存在。创建锁“1_332”。我们并不在堆栈顶部,因此我们将等待
do/until
并继续检查,直到我们排在第一位。 - 文件 1 复制完毕。删除锁“0_19831”。
- 下载文件 3 并启动脚本。锁已存在。创建锁“2_7582”。我们尚未到达堆栈顶部,请等待。
- 文件 2 复制完毕。删除锁“1_332”。
- 文件 3 复制完毕。删除锁“2_7582”。
这个解决方案不是万无一失的,但根据规模可能有效。