2011-10-05 23 views
7

Khi gỡ lỗi một ứng dụng sử dụng Semaphores để đồng bộ hóa chéo, tôi tình cờ sử dụng PowerShell để thay thế cho quá trình "khác". Làm một cái gì đó như thế này trong PowerShell hoạt động tốt:Tương tác bằng Mutexes (et al) trong 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() 

Và tôi có thể gọi WaitOne()Release() nhiều lần trên cùng một thể hiện của một Semaphore, thường xuyên như tôi cần.

Nhưng khi tôi cố gắng làm điều tương tự với một Mutex, PowerShell tiếp tục tuyên bố rằng các mutex bị bỏ rơi:

[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 

Các lỗi có thể xảy ra bất cứ lúc nào tôi gọi WaitOne() sau khi đã mua lại mutex một lần trước , hoặc là một WaitOne gọi trước hoặc yêu cầu cho nó được ban đầu thuộc sở hữu trong 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 

là Powershell làm một số shenanigans chủ đề lạ trong nền hay tôi chỉ hoàn toàn quên cách Mutexes w ork?

Trả lời

25

Theo mặc định, powershell v2.0 (tại bàn điều khiển, không phải là ISE đồ họa) sử dụng một luồng MTA. Điều này có nghĩa là mỗi dòng tương tác được thực thi trên một thread khác nhau:

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

Tuy nhiên, một kịch bản không tương tác sẽ chạy theo một chủ đề duy nhất, đó là để nói, thread đó gọi lệnh để chạy nó :

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

Nếu bạn muốn chạy PowerShell tương tác với một chuỗi, hãy khởi động trình bao bằng khóa -STA. Bạn có thể làm điều này tương tác:

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

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

Như bạn thấy, PowerShell sẽ sử dụng căn hộ một luồng để thực thi các lệnh tương tác. Điều này thường là sự lựa chọn mong muốn để làm việc với WPF hoặc WinForms, hoặc nếu bạn muốn chơi với mutexes toàn hệ thống:

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

Btw, powershell v3 (vận chuyển với windows 8 và downlevel cũng có sẵn trên 7 win) sử dụng -STA làm chế độ mặc định cho bảng điều khiển. ISE powerhell đồ họa luôn sử dụng -STA, cả trong v2 và v3.

+0

+1 - Tôi chuẩn bị bình luận về v3.0, vì ví dụ của OP làm việc cho tôi trên 3.0 CTP, và tôi nhận ra v3.0 (console) là STA theo mặc định. Sau đó, thấy rằng bạn cũng đã làm điều đó :) – manojlds

+0

+1 Xóa mẫu. Câu trả lời rõ ràng. –

+0

Bạn không thể chỉ cung cấp tên mutex "toàn cầu", bất kể MTA/STA của cá thể Powershell là gì? – Signal15

1

Sử dụng chủ đề này là nguồn cảm hứng, xây dựng một thực hiện đơn giản của một cơ chế quá trình khóa sử dụng Mutexes trong 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 này giúp ai đó.

Các vấn đề liên quan