Chcę się upewnić, że to tam jest, ponieważ tak trudno jest to naprawić:
using System.Runtime.InteropServices; //GuidAttribute
using System.Reflection; //Assembly
using System.Threading; //Mutex
using System.Security.AccessControl; //MutexAccessRule
using System.Security.Principal; //SecurityIdentifier
static void Main(string[] args)
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid =
((GuidAttribute)Assembly.GetExecutingAssembly().
GetCustomAttributes(typeof(GuidAttribute), false).
GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format( "Global\\{{{0}}}", appGuid );
// Need a place to store a return value in Mutex() constructor call
bool createdNew;
// edited by Jeremy Wiebe to add example of setting up security for multi-user usage
// edited by 'Marc' to work also on localized systems (don't use just "Everyone")
var allowEveryoneRule =
new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid
, null)
, MutexRights.FullControl
, AccessControlType.Allow
);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
// edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen
using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
{
// edited by acidzombie24
var hasHandle = false;
try
{
try
{
// note, you may want to time out here instead of waiting forever
// edited by acidzombie24
// mutex.WaitOne(Timeout.Infinite, false);
hasHandle = mutex.WaitOne(5000, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access");
}
catch (AbandonedMutexException)
{
// Log the fact that the mutex was abandoned in another process,
// it will still get acquired
hasHandle = true;
}
// Perform your work here.
}
finally
{
// edited by acidzombie24, added if statement
if(hasHandle)
mutex.ReleaseMutex();
}
}
}
using
zaznaczeniecreatedNew
i dodanie domutex.Dispose()
środkafinally
. Nie mogę tego teraz jasno wyjaśnić (nie znam powodu), ale doszedłem do sytuacji, gdymutex.WaitOne
wróciłemtrue
po tym, jak sięcreatedNew
stałfalse
(nabyłem muteks w bieżącym,AppDomain
a następnie załadowałem nowyAppDomain
i wykonałem ten sam kod z w tym).exitContext = false
coś robimutex.WaitOne(5000, false)
? Wygląda na to, że może to spowodować tylko twierdzenie w CoreCLR , 2. Jeśli ktoś zastanawia się, wMutex
konstruktorze, powód, dla któregoinitiallyOwned
jestfalse
to częściowo wyjaśnione w tym artykule MSDN .Korzystając z zaakceptowanej odpowiedzi, tworzę klasę pomocnika, abyś mógł użyć jej w podobny sposób, jak przy użyciu instrukcji Lock. Pomyślałem, że się podzielę.
Posługiwać się:
I klasa pomocnicza:
źródło
< 0
zamiast<= 0
._mutex.Close()
zamiast_mutex.Dispose()
metody Dispose działało dla mnie. Błąd był spowodowany próbą pozbycia się bazowego WaitHandle.Mutex.Close()
pozbywa się podstawowych zasobów.W zaakceptowanej odpowiedzi występuje warunek wyścigu, gdy 2 procesy uruchomione przez 2 różnych użytkowników próbują zainicjować muteks w tym samym czasie. Po pierwszym zainicjowaniu muteksu przez pierwszy proces, jeśli drugi proces spróbuje zainicjować muteks, zanim pierwszy proces ustawi dla wszystkich regułę dostępu, drugi proces zgłosi nieautoryzowany wyjątek.
Poprawiona odpowiedź znajduje się poniżej:
źródło
Ten przykład zakończy się po 5 sekundach, jeśli inna instancja jest już uruchomiona.
źródło
Ani Mutex, ani WinApi CreateMutex () nie działa dla mnie.
Alternatywne rozwiązanie:
Oraz
SingleApplicationDetector
:Powód, aby używać Semafora zamiast Mutex:
Ref: Semaphore.OpenExisting ()
źródło
Semaphore.OpenExisting
inew Semaphore
.Czasami uczenie się na przykładzie najbardziej pomaga. Uruchom tę aplikację konsoli w trzech różnych oknach konsoli. Zobaczysz, że aplikacja, którą najpierw uruchomiłeś, najpierw nabywa muteks, podczas gdy pozostałe dwie czekają na swoją kolej. Następnie naciśnij klawisz Enter w pierwszej aplikacji, zobaczysz, że aplikacja 2 kontynuuje działanie po zdobyciu muteksu, jednak aplikacja 3 czeka na swoją kolej. Po naciśnięciu klawisza Enter w aplikacji 2 zobaczysz, że aplikacja 3 trwa. Ilustruje to koncepcję muteksu chroniącego sekcję kodu, która ma być wykonywana tylko przez jeden wątek (w tym przypadku proces), np. Zapis do pliku jako przykład.
źródło
Globalny Mutex ma zapewnić nie tylko jedną instancję aplikacji. Osobiście wolę używać Microsoft.VisualBasic, aby zapewnić aplikację z pojedynczą instancją, jak opisano w Jaki jest prawidłowy sposób tworzenia aplikacji WPF z jedną instancją? (Odpowiedź Dale'a Ragana) ... Odkryłem, że łatwiej jest przekazywać argumenty otrzymane podczas uruchamiania nowej aplikacji do początkowej aplikacji pojedynczej instancji.
Ale jeśli chodzi o niektóre poprzednie kody w tym wątku, wolałbym nie tworzyć Mutex za każdym razem, gdy chcę mieć blokadę. Może to być w porządku dla aplikacji z pojedynczą instancją, ale w innym zastosowaniu wydaje mi się, że ma nadmiar.
Dlatego zamiast tego sugeruję tę implementację:
Stosowanie:
Globalne opakowanie Mutex:
Kelner
źródło
Rozwiązanie (dla WPF) bez WaitOne, ponieważ może powodować wyjątek AbandonedMutexException. To rozwiązanie korzysta z konstruktora Mutex, który zwraca wartość utworzonej wartości logicznej, aby sprawdzić, czy muteks jest już utworzony. Wykorzystuje również GetType (). GUID, więc zmiana nazwy pliku wykonywalnego nie pozwala na wiele wystąpień.
Muteks globalny a lokalny patrz uwaga w: https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8
Ponieważ Mutex implementuje IDisposable, jest zwalniany automatycznie, ale dla kompletności wywołaj dispose:
Przenieś wszystko do klasy bazowej i dodaj allowEveryoneRule z zaakceptowanej odpowiedzi. Dodałem także ReleaseMutex, choć nie wygląda na to, żeby był naprawdę potrzebny, ponieważ jest uwalniany automatycznie przez system operacyjny (co jeśli aplikacja się zawiesi i nigdy nie wywoła ReleaseMutex, czy konieczne byłoby ponowne uruchomienie?).
źródło