在 Powershell 中交互使用互斥体(等)

发布于 2024-12-08 04:19:03 字数 1984 浏览 1 评论 0原文

在调试使用信号量进行跨进程同步的应用程序时,我偶然发现了使用 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()

但是,当我尝试对互斥体执行相同的操作时,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 是否在后台执行一些奇怪的线程恶作剧,或者我完全忘记了互斥体是如何工作的?

While debugging an application that uses Semaphores for cross-process synchronization, I stumbled upon the idea of using PowerShell to take the place of the "other" process. Doing something like this in PowerShell works fine:

// 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()

And I can call WaitOne() and Release() repeatedly on that same instance of a Semaphore, as often as I need to.

But when I try to do the same thing with a Mutex, PowerShell keeps claiming that the mutex was abandoned:

[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

The error seems to happen any time I call WaitOne() after having acquired the mutex once before, either a previous WaitOne call or asking for it to be initially owned in the constructor:

[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

Is Powershell doing some wierd thread shenanigans in the background or am I just completely forgetting how Mutexes work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青朷 2024-12-15 04:19:03

默认情况下,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 始终使用 -STA,无论是在 v2 还是 v3 中。

By default, powershell v2.0 (at the console, not the graphical ISE) uses an MTA threadpool. What this means is that each interactive line is executed on a different thread:

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

However, a non-interactive script will run under a single thread, that is to say, the thread that invoked the command to run it:

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

If you want to run powershell interactively with a single thread, start the shell with the -STA switch. You can do this interactively:

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

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

As you can see, powershell will use a single-threaded apartment to execute interactive commands. This is usually the desired choice for working with WPF or WinForms, or if you want to play with system-wide mutexes:

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

Btw, powershell v3 (shipping with windows 8 and also available downlevel on win 7) uses -STA as the default mode for the console. The graphical powershell ISE always uses -STA, both in v2 and v3.

二货你真萌 2024-12-15 04:19:03

以此线程为灵感,在 powershell 中使用互斥体构建了进程锁定机制的简单实现:

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()

希望这对某人有帮助。

Using this thread as inspiration, built a simple implementation of a process locking mechanism using Mutexes in powershell:

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()

Hope This helps somebody.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文