在Powershell中交互式使用Mutexes(等)

时间:2011-10-05 16:21:48

标签: .net multithreading powershell

在调试使用Semaphores进行跨进程同步的应用程序时,我偶然发现了使用PowerShell取代“其他”进程的想法。在PowerShell中做这样的事情很好:

// In C# application:
var sem = new Semaphore(0, 1, "CrossProcSem");
sem.WaitOne();

# In PowerShell session:
[1] C:\Projects $ $sem = New-Object System.Threading.Semaphore(0, 1, "CrossProcSem")
[2] C:\Projects $ $sem.Release()

我可以根据需要在信号量的同一个实例上反复调用WaitOne()Release()

但是当我尝试使用Mutex做同样的事情时,PowerShell一直声称该互斥锁被放弃了:

[1] C:\Projects $ $mtx = New-Object System.Threading.Mutex($false, "CrossProcMtx")
[2] C:\Projects $ $mtx.WaitOne()
True
[3] C:\Projects $ $mtx.ReleaseMutex()
[4] C:\Projects $ $mtx.WaitOne()
Exception calling "WaitOne" with "0" argument(s): "The wait completed due to an abandoned mutex."
At line:1 char:13
+ $mtx.WaitOne <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : DotNetMethodException

错误似乎发生在我之前获得过一次互斥锁之前调用WaitOne()之后,无论是之前的WaitOne调用还是要求它最初在构造函数中拥有:

[5] C:\Projects $ $mtx2 = New-Object System.Threading.Mutex($true)
[6] C:\Projects $ $mtx2.WaitOne()
Exception calling "WaitOne" with "0" argument(s): "The wait completed due to an abandoned mutex."
At line:1 char:14
+ $mtx2.WaitOne <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : DotNetMethodException

[7] C:\Projects $ $mtx3 = New-Object System.Threading.Mutex
[8] C:\Projects $ $mtx3.WaitOne()
True
[9] C:\Projects $ $mtx3.WaitOne()
Exception calling "WaitOne" with "0" argument(s): "The wait completed due to an abandoned mutex."
At line:1 char:14
+ $mtx3.WaitOne <<<< ()
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : DotNetMethodException

Powershell是在后台做了一些奇怪的线程诡计,还是我完全忘记了互斥锁是如何工作的?

2 个答案:

答案 0 :(得分:25)

默认情况下,powershell v2.0(在控制台,而不是图形ISE)使用MTA线程池。这意味着每个交互式行在不同的线程上执行:

PS> [threading.thread]::CurrentThread.ManagedThreadId
13
PS> [threading.thread]::CurrentThread.ManagedThreadId
10
PS> [threading.thread]::CurrentThread.ManagedThreadId
8
PS> [threading.thread]::CurrentThread.ManagedThreadId
4

但是,非交互式脚本将在单个线程下运行,也就是说,调用该命令来运行它的线程:

PS> $script = {
>> [threading.thread]::CurrentThread.ManagedThreadId
>> [threading.thread]::CurrentThread.ManagedThreadId
>> [threading.thread]::CurrentThread.ManagedThreadId
>> [threading.thread]::CurrentThread.ManagedThreadId
>> }
>>
PS> & $script
16
16
16
16

如果要使用单个线程以交互方式运行powershell,请使用-STA开关启动shell。您可以以交互方式执行此操作:

PS> powershell -sta
Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS> $host.runspace | select threadoptions, apartmentstate
ThreadOptions     ApartmentState
-------------     --------------
ReuseThread                  STA

如您所见,powershell将使用单线程单元来执行交互式命令。这通常是使用WPF或WinForms的理想选择,或者如果您想使用系统范围的互斥锁:

PS> $mtx = New-Object System.Threading.Mutex($false, "CrossProcMtx")
PS> $mtx.WaitOne()
True
PS> $mtx.ReleaseMutex()
PS> $mtx.WaitOne()
True

顺便说一句,powershell v3(与Windows 8一起发布,在win 7上也可以下载)使用-STA作为控制台的默认模式。图形PowerShell ISE始终在v2和v3中使用-STA。

答案 1 :(得分:1)

使用这个线程作为灵感,在powershell中使用Mutexes构建了一个简单的进程锁定机制实现:

function Wait-OnMutex
{
    param(
    [parameter(Mandatory = $true)][string] $MutexId
    )

    try
    {
        $MutexInstance = New-Object System.Threading.Mutex -ArgumentList 'false', $MutexId

        while (-not $MutexInstance.WaitOne(1000))
        {
            Start-Sleep -m 500;
        }

        return $MutexInstance
    } 
    catch [System.Threading.AbandonedMutexException] 
    {
        $MutexInstance = New-Object System.Threading.Mutex -ArgumentList 'false', $MutexId
        return Wait-OnMutex -MutexId $MutexId
    }

}

##
## main method

$MutexInstance = Wait-OnMutex -MutexId 'SomeMutexId12345'

Write-Host "my turn"
#this is where you do work inside the "lock"
Read-Host 

$MutexInstance.ReleaseMutex()

希望这有助于某人。