2011-10-05 32 views
7

Al depurar una aplicación que usa semáforos para la sincronización de procesos cruzados, me encontré con la idea de usar PowerShell para tomar el lugar del "otro" proceso. Hacer algo como esto en PowerShell funciona bien:Interactivamente usando Mutexes (et al) en 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() 

Y puede llamar WaitOne() y Release() varias veces en la misma instancia de un semáforo, tantas veces como lo necesite.

Pero cuando trato de hacer lo mismo con un objeto mutex, PowerShell continúa afirmando que el mutex fue abandonado:

[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 

El error parece ocurrir en cualquier momento que llamo WaitOne() después de haber adquirido el mutex una vez antes , ya sea una llamada anterior WaitOne o pidiendo que sea propiedad inicialmente en el 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 haciendo algunas travesuras hilo extraño en el fondo o solo estoy olvidando por completo la forma mutex w ork?

Respuesta

25

De forma predeterminada, PowerShell v2.0 (en la consola, no en el ISE gráfico) utiliza un subproceso MTA. Lo que esto significa es que cada línea interactiva se ejecuta en un subproceso diferente:

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

Sin embargo, un script no interactivo se ejecutará en un solo hilo, es decir, el hilo que invoca el comando para ejecutarlo :

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

Si desea ejecutar powershell de forma interactiva con un solo hilo, inicie el shell con el modificador -STA. Usted puede hacer esto de forma interactiva:

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

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

Como se puede ver, PowerShell utilizar un apartamento de un único subproceso para ejecutar comandos interactivos. Esta suele ser la opción deseada para trabajar con WPF o WinForms, o si desea jugar con todo el sistema mutex:

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

Por cierto, v3 PowerShell (envío con Windows 8 y de nivel inferior también está disponible en 7 WIN) usos -STA como el modo predeterminado para la consola. El ISE gráfico de powershell siempre usa -STA, tanto en v2 como en v3.

+0

+1 - Estaba a punto de comentar sobre v3.0, ya que el ejemplo de OP me funcionó en el 3.0 CTP, y me di cuenta de que v3.0 (consola) era STA de forma predeterminada. Luego vi que hiciste eso también :) – manojlds

+0

+1 Borra muestras. Respuesta clara. –

+0

¿No puede simplemente proporcionar un nombre mutex "global", independientemente del MTA/STA de la instancia de Powershell? – Signal15

1

El uso de este hilo de inspiración, construyeron una implementación sencilla de un mecanismo de bloqueo proceso usando mutex en 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() 

espero que esto ayude a alguien.

Cuestiones relacionadas