2013-03-26T23:33:46の更新内容

programming/netfx/fcl/System.Threading.Mutex/index.wiki.txt

current previous
1,13 1,10
 
${smdncms:title,System.Threading.Mutex}
${smdncms:title,System.Threading.Mutex}
 
${smdncms:keywords,System.Threading,Mutex}
${smdncms:keywords,System.Threading,Mutex}
+
${smdncms:document_versions,codelang=cs,codelang=vb}
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/netfx/fcl/System.Threading.Semaphore]]
--[[programming/netfx/fcl/System.Threading.Semaphore]]
 
--[[programming/netfx/fcl/System.Threading.WaitHandle]]
--[[programming/netfx/fcl/System.Threading.WaitHandle]]
 
--[[programming/netfx/fcl/System.Threading.Monitor]]
--[[programming/netfx/fcl/System.Threading.Monitor]]
+
--[[programming/netfx/tips/singleinst_with_mutex]]
+
--[[programming/mono/tips#MONO_ENABLE_SHM]] (Mono)
 

        

        
 
*ミューテックスとは
*ミューテックスとは
 
ミューテックス(Mutex = MUTual EXclusion)とは排他制御を行う上で用いられる概念のひとつ。 ミューテックスは、ミューテックス(=排他区間)へ入ることを要求するP操作、ミューテックスから出ることを通知するV操作から構成される。 ミューテックスは同時に1つの処理しか排他区間に入ることを許可しないため、カウンタの初期値が1であるセマフォと同義である。 セマフォについての詳細は[[programming/netfx/fcl/System.Threading.Semaphore]]にて解説している。
ミューテックス(Mutex = MUTual EXclusion)とは排他制御を行う上で用いられる概念のひとつ。 ミューテックスは、ミューテックス(=排他区間)へ入ることを要求するP操作、ミューテックスから出ることを通知するV操作から構成される。 ミューテックスは同時に1つの処理しか排他区間に入ることを許可しないため、カウンタの初期値が1であるセマフォと同義である。 セマフォについての詳細は[[programming/netfx/fcl/System.Threading.Semaphore]]にて解説している。
24,16 21,14
 
「所有権」の有無はインスタンスの生成時に指定できる(初期所有権)。 初期所有権を付与しなかった場合は、システムミューテックスを生成したスレッドが終了した後に他のスレッドが放棄されたミューテックスに入ろうとしても、AbandonedMutexExceptionは発生しない。
「所有権」の有無はインスタンスの生成時に指定できる(初期所有権)。 初期所有権を付与しなかった場合は、システムミューテックスを生成したスレッドが終了した後に他のスレッドが放棄されたミューテックスに入ろうとしても、AbandonedMutexExceptionは発生しない。
 

        

        
 
*主なコンストラクタ
*主なコンストラクタ
~
:Mutex()|名前なしミューテックスのインスタンスを生成する。
:Mutex|名前なしミューテックスのインスタンスを生成する。
~
:Mutex(bool &var{initiallyOwned};, string &var{name};)|初期所有権の有無、ミューテックスの名前を指定してインスタンスを生成する。
:Mutex(bool, string)|初期所有権の有無、ミューテックスの名前を指定してインスタンスを生成する。
+
:Mutex(bool &var{initiallyOwned};, string &var{name};, out bool &var{createdNew};)|初期所有権の有無、ミューテックスの名前を指定してインスタンスを生成する。
+
新しく名前付きミューテックスを作成した場合は&var{createdNew};にtrue、すでに同名の名前付きミューテックスが存在している場合はfalseがセットされる。
 

        

        
 
*主なメソッド
*主なメソッド
~
:static Mutex &msdn(netfx,member,System.Threading.Mutex.OpenExisting){OpenExisting};(string &var{name};)|すでに作成されている名前付きミューテックスを取得する。 取得できない場合、存在しない場合は、WaitHandleCannotBeOpenedExceptionがスローされる。
:static Mutex &msdn(netfx,member,System.Threading.Mutex.OpenExisting){OpenExisting(string)};|すでに作成されている名前付きミューテックスを取得する。 取得できない場合、存在しない場合は、WaitHandleCannotBeOpenedExceptionがスローされる。
~
:bool &msdn(netfx,member,System.Threading.Mutex.WaitOne){WaitOne};()|ミューテックスに入る(P操作)。 すでに他のスレッドがミューテックスに入っている場合は、ReleaseMutex()が呼ばれる(V操作が行われる)まで処理がブロックされる。
:bool &msdn(netfx,member,System.Threading.Mutex.WaitOne){WaitOne()};|ミューテックスに入る(P操作)。 すでに他のスレッドがミューテックスに入っている場合は、ReleaseMutex()が呼ばれる(V操作が行われる)まで処理がブロックされる。
~
:void &msdn(netfx,member,System.Threading.Mutex.ReleaseMutex){ReleaseMutex};()|ミューテックスから出る(V操作)。
:void &msdn(netfx,member,System.Threading.Mutex.ReleaseMutex){ReleaseMutex()};|ミューテックスから出る(V操作)。
~
:void &msdn(netfx,member,System.Threading.Mutex.Close){Close};()|インスタンスが保持しているリソースを解放する。
:void &msdn(netfx,member,System.Threading.Mutex.OpenExisting){Close()};|インスタンスが保持しているリソースを解放する。
 

        

        
 
*主な呼び出し順序
*主な呼び出し順序
 
ローカルミューテックスの場合。
ローカルミューテックスの場合。
51,139 46,113
 
*使用例
*使用例
 
システムミューテックスを生成し、排他区間に対して複数のプロセスから同時にアクセスを試みる。
システムミューテックスを生成し、排他区間に対して複数のプロセスから同時にアクセスを試みる。
 

        

        
~
#tabpage(codelang=cs)
#code(cs){{
+
#code{{
 
using System;
using System;
-
using System.Diagnostics;
 
using System.Threading;
using System.Threading;
+
using System.Diagnostics;
 

        

        
~
class Sample
class SystemMutexSample
 
{
{
-
  // 名前付きシステムミューテックス(プロセス間で共有する)
-
  private Mutex mutex = null;
-

          
-
  //
-
  // エントリーポイント
-
  //
 
  public static void Main()
  public static void Main()
 
  {
  {
~
    var processId = Process.GetCurrentProcess().Id;
    Console.WriteLine( "mutex sample start" );
 

        

        
~
    Console.WriteLine("{0} started", processId);
    ( new SystemMutexSample() ).Run();
-
  }
 

        

        
~
    // プロセス間で共有する名前付きミューテックスを作成(初期所有権は付与しない)
  public void Run()
~
    const string mutexName = "named-mutex-sample";
  {
~
    bool createdNew;
    Process proc = Process.GetCurrentProcess();
 

        

        
~
    using (var mutex = new Mutex(false, mutexName, out createdNew)) {
    // ミューテックス・インスタンスを作成
~
      // このプロセスで名前付きミューテックスを生成した
    try
~
      if (createdNew)
    {
~
        Console.WriteLine("{0} mutex created", processId);
      // すでに存在するシステムミューテックスを取得する
-
      mutex = Mutex.OpenExisting( "SystemMutexSample" );
-
    }
-
    catch ( WaitHandleCannotBeOpenedException )
-
    {
-
      // 取得できなかった場合は、新たにインスタンスを作成する
-
      // (初期所有権は付与しない)
-
      mutex = new Mutex( false, "SystemMutexSample" );
 

        

        
~
      Console.WriteLine("{0} waiting", processId);
      // このプロセスで名前付きシステムミューテックスを生成した
~

          
      Console.WriteLine( "new mutex created by PID:{0:X8}.", proc.Id );
+
      // ミューテックスに入る (入れるまで処理をブロックする)
+
      mutex.WaitOne();
+

          
+
      Console.WriteLine("{0} -> enter", processId);
+

          
+
      // 完了するまで時間のかかる処理を想定
+
      Thread.Sleep((new Random(processId)).Next(100, 500));
+

          
+
      // ミューテックスを出る
+
      mutex.ReleaseMutex();
+

          
+
      Console.WriteLine("{0} <- exited", processId);
 
    }
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Threading
+
Imports System.Diagnostics
 

        

        
~
Class Sample
    // 他のプロセスが起動するまで適当な時間だけ待ち合わせる
~
  Public Shared Sub Main()
    Thread.Sleep( new Random( proc.Id ).Next( 500, 5000 ) );
+
    Dim processId As Integer = Process.GetCurrentProcess().Id
 

        

        
~
    Console.WriteLine("{0} started", processId)
    Console.WriteLine( "ProcessID {0:X8}, wait for  mutex", proc.Id );
 

        

        
~
    ' プロセス間で共有する名前付きミューテックスを作成(初期所有権は付与しない)
    // ミューテックスに入る
~
    Const mutexName As String = "named-mutex-sample"
    mutex.WaitOne();
+
    Dim createdNew As Boolean
 

        

        
~
    Using mutex As New Mutex(False, mutexName, createdNew)
    Console.WriteLine( "ProcessID {0:X8}, access to mutex", proc.Id );
+
      ' このプロセスで名前付きミューテックスを生成した
+
      If createdNew Then  Console.WriteLine("{0} mutex created", processId)
 

        

        
~
      Console.WriteLine("{0} waiting", processId)
    // 適当な時間だけ待ち合わせる
-
    Thread.Sleep( new Random( proc.Id ).Next( 100, 500 ) );
 

        

        
~
      ' ミューテックスに入る (入れるまで処理をブロックする)
    Console.WriteLine( "ProcessID {0:X8}, release   mutex", proc.Id );
+
      mutex.WaitOne()
 

        

        
~
      Console.WriteLine("{0} -> enter", processId)
    // ミューテックスを出る
-
    mutex.ReleaseMutex();
 

        

        
~
      ' 完了するまで時間のかかる処理を想定
    // プロセス終了
~
      Thread.Sleep((New Random(processId)).Next(100, 500))
    return;
~

          
  }
~
      ' ミューテックスを出る
}
+
      mutex.ReleaseMutex()
+

          
+
      Console.WriteLine("{0} <- exited", processId)
+
    End Using
+
  End Sub
+
End Class
 
}}
}}
+
#tabpage-end
+

          
+
上記プログラムを5プロセス同時に実行した場合の動作例。
+

          
+
#prompt(Windows + .NET Frameworkでの動作結果例){{
+
>csc test.cs
+
>test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe
+
2328 started
+
2328 mutex created
+
2328 waiting
+
2328 -> enter
+
3952 started
+
3720 started
+
3952 waiting
+
3720 waiting
+
4624 started
+
4624 waiting
+
4940 started
+
4940 waiting
+
2328 <- exited
+
4940 -> enter
+
3720 -> enter
+
4940 <- exited
+
4624 -> enter
+
3720 <- exited
+
4624 <- exited
+
3952 -> enter
+
3952 <- exited
+
}}
+

          
+
Monoでは、[[環境変数MONO_ENABLE_SHMをセットして共有メモリの使用を有効に>programming/mono/tips#MONO_ENABLE_SHM]]しないと名前付きミューテックスを使用できない。
+

          
+
#prompt(Linux + Monoでの動作結果例){{
+
$ mcs test.cs && for i in `seq 1 5` ; do MONO_ENABLE_SHM=1 mono test.exe & done
 

        

        
~
22269 started
上記プログラムを10プロセス同時に実行した場合の例(結果はMono 1.2.3.1で実行したときのもの)
~
22269 mutex created
#prompt{{
~
22269 waiting
$ for i in `seq 1 10` ; do mono sample.exe &  done
~
22269 -> enter

          
~
22267 started
mutex sample start
~
22267 waiting
mutex sample start
~
22265 started
new mutex created by PID:000064E5.
~
22265 waiting
mutex sample start
~
22268 waiting
mutex sample start
~
22266 started
mutex sample start
~
22266 waiting
mutex sample start
~
22269 <- exited
mutex sample start
~
22267 -> enter
mutex sample start
~
22267 <- exited
mutex sample start
~
22268 -> enter
ProcessID 000064E8, wait for  mutex
~
22268 <- exited
ProcessID 000064E8, access to mutex
~
22265 -> enter
ProcessID 000064E8, release   mutex
~
22265 <- exited
ProcessID 000064E6, wait for  mutex
~
22266 -> enter
ProcessID 000064E6, access to mutex
~
22266 <- exited
ProcessID 000064F1, wait for  mutex
-
ProcessID 000064E6, release   mutex
-
ProcessID 000064F1, access to mutex
-
ProcessID 000064F1, release   mutex
-
ProcessID 000064EF, wait for  mutex
-
ProcessID 000064EF, access to mutex
-
ProcessID 000064EF, release   mutex
-
ProcessID 000064E9, wait for  mutex
-
ProcessID 000064E9, access to mutex
-
ProcessID 000064E7, wait for  mutex
-
ProcessID 000064F2, wait for  mutex
-
ProcessID 000064E9, release   mutex
-
ProcessID 000064E7, access to mutex
-
ProcessID 000064E5, wait for  mutex
-
ProcessID 000064E7, release   mutex
-
ProcessID 000064F0, wait for  mutex
-
ProcessID 000064F0, access to mutex
-
ProcessID 000064EE, wait for  mutex
-
ProcessID 000064F0, release   mutex
-
ProcessID 000064F2, access to mutex
-
ProcessID 000064F2, release   mutex
-
ProcessID 000064E5, access to mutex
-
ProcessID 000064E5, release   mutex
-
ProcessID 000064EE, access to mutex
-
ProcessID 000064EE, release   mutex
 
}}
}}
 

        

        

programming/netfx/tips/singleinst_with_mutex/index.wiki.txt

current previous
1,115 1,84
 
${smdncms:title,Mutexを利用した多重起動の禁止}
${smdncms:title,Mutexを利用した多重起動の禁止}
~
${smdncms:keywords,System.Threading,Mutex,多重起動,名前付きミューテックス}
${smdncms:keywords,System.Threading,Mutex,多重起動}
 
${smdncms:tags,lang/c#,lang/vb,api/.net}
${smdncms:tags,lang/c#,lang/vb,api/.net}
 
${smdncms:document_versions,codelang=cs,codelang=vb}
${smdncms:document_versions,codelang=cs,codelang=vb}
 

        

        
~
-関連するページ
#googleadunit
+
--[[programming/netfx/fcl/System.Threading.Mutex]]
+
--[[programming/netfx/fcl/System.Threading.WaitHandle]]
+
--[[programming/mono/tips#MONO_ENABLE_SHM]] (Mono)
 

        

        
~
[[Mutexクラス>programming/netfx/fcl/System.Threading.Mutex]]を使うことにより、同時に1つの処理のみが動作するように排他制御することができる。 Mutexクラスでは''名前付きミューテックス''を作成することができ、ミューテックスをプロセス間で共有できる。 そのため、名前付きミューテックスを使った排他制御を行うことでアプリケーションの多重起動を抑止できる。
Mutexクラスの説明
~

          
#code(pl){{
~
Mutexクラスでは、コンストラクタに適切な文字列を指定することにより名前付きミューテックスを作成することができる。 指定する文字列には&msdn(netfx,member,System.Windows.Forms.dll,System.Windows.Forms.Application.ProductName){Application.ProductName};や、あらかじめ用意したGUIDなど、他のアプリケーションが使用していない文字列を選択する。
オーバーロード。 派生クラスでオーバーライドされると、現在のWaitHandleがシグナルを受信するまで現在のスレッドをブロックします。
~

          
Overloads Public Overridable Function WaitOne( _
~
#googleadunit
  ByVal millisecondsTimeout As Integer, _
-
  ByVal exitContext As Boolean _
-
) As Boolean
-
}}
-
:millisecondsTimeout|スレッドによるシグナルの受信を待機するミリ秒数。
-
:exitContext|待機する前にコンテキストの同期ドメインを終了し (同期されたコンテキストの場合)、再び取得する場合は true 。それ以外の場合は false 。
-
:戻り値|現在のインスタンスがシグナルを受け取る場合は true 。それ以外の場合は false。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
+
using System;
 
using System.Threading;
using System.Threading;
-
using System.Windows.Forms;
 

        

        
~
class Sample
public static void Main()
 
{
{
~
  public static void Main()
  // Mutexのインスタンスを生成する
-
  Mutex m = new Mutex(false, Application.ProductName);
-

          
-
  // 既にMutexが取得されていないか確認する
-
  if ( !m.waitone(0, false) )
 
  {
  {
~
    // 名前付きミューテックスに与える名前
    // 既に取得されている場合
~
    const string mutexName = "MyApplication";
    Console.WriteLine( "Application already executed.");
-

          
-
    // mutexを解放
-
    m.Close();
 

        

        
~
    // 初期所有権は与えず、名前付きミューテックスを作成
    // ここまでで処理を終了する
~
    using (var mutex = new Mutex(false, mutexName)) {
    return;
+
      // ミューテックスに入る
+
      // (すでに他のプロセスがミューテックスの排他区間に入っている場合でも、待機せず即座に処理を返す)
+
      if (mutex.WaitOne(0, false)) {
+
        // ミューテックスに入った
+
        // (このプロセス以外にミューテックスの排他区間に入っているプロセスはない)
+
        Console.WriteLine("起動します");
+

          
+
        // 以下、アプリケーションの処理を続行する
+
        //Application.Run(new Form1());
+
        Thread.Sleep(3000);
+

          
+
        Console.WriteLine("終了しました");
+
      }
+
      else {
+
        // ミューテックスに入れなかった
+
        // (このプロセス以外にミューテックスの排他区間に入っているプロセスがある)
+
        Console.WriteLine("既に起動しています");
+
      }
+
    }
 
  }
  }
-

          
-
  // 複数起動されていない場合
-
  Console.WriteLine("run application");
-

          
-
  // mutexを解放
-
  m.ReleaseMutex();
 
}
}
 
}}
}}
 
#tabpage(codelang=vb)
#tabpage(codelang=vb)
 
#code{{
#code{{
+
Imports System
 
Imports System.Threading
Imports System.Threading
-
Imports System.Windows.Forms
-

          
-
Public Shared Sub Main()
 

        

        
~
Class Sample
  ' Mutexのインスタンスを生成する
~
  Shared Sub Main()
  Dim m As New Mutex(False, Application.ProductName)
~
    ' 名前付きミューテックスに与える名前

          
~
    Const mutexName As String = "MyApplication"
  ' 既にMutexが取得されていないか確認する
~

          
  If Not m.waitone(0, false) Then 
~
    ' 初期所有権は与えず、名前付きミューテックスを作成

          
~
    Using mutex As New Mutex(False, mutexName)
    ' 既に取得されている場合
~
      ' ミューテックスに入る
    Console.Writeline( "Application already executed.")
~
      ' (すでに他のプロセスがミューテックスの排他区間に入っている場合でも、待機せず即座に処理を返す)

          
~
      If mutex.WaitOne(0, False) Then
    ' mutexを解放
~
        ' ミューテックスに入った
    m.Close()
~
        ' (このプロセス以外にミューテックスの排他区間に入っているプロセスはない)

          
~
        Console.WriteLine("起動します")
   ' ここまでで処理を終了する
~

          
   Return 
+
        ' 以下、アプリケーションの処理を続行する
+
        'Application.Run(New Form1())
+
        Thread.Sleep(3000)
+

          
+
        Console.WriteLine("終了しました")
+
      Else
+
        ' ミューテックスに入れなかった
+
        ' (このプロセス以外にミューテックスの排他区間に入っているプロセスがある)
+
        Console.WriteLine("既に起動しています")
+
      End If
+
    End Using
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
-
  End if
 

        

        
~
#prompt(Windows + .NET Frameworkでの動作結果例){{
  ' 複数起動されていない場合
~
>csc test.cs
  Console.WriteLine("run application")
+
>test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe
+
起動します
+
既に起動しています
+
既に起動しています
+
既に起動しています
+
既に起動しています
+
終了しました
+
}}
 

        

        
~
Monoでは、[[環境変数MONO_ENABLE_SHMをセットして共有メモリの使用を有効に>programming/mono/tips#MONO_ENABLE_SHM]]しないと名前付きミューテックスを使用できない。
  ' mutexを解放
-
  m.ReleaseMutex()
 

        

        
~
#prompt(Linux + Monoでの動作結果例){{
End Sub 
+
$ mcs test.cs && for i in `seq 1 5` ; do MONO_ENABLE_SHM=1 mono test.exe & done
+
起動します
+
既に起動しています
+
既に起動しています
+
既に起動しています
+
既に起動しています
+
終了しました
 
}}
}}
-
#tabpage-end
 

        

        
~
-参考 (ローカル宣言したMutexのGC.KeepAliveは必要か)
Mutexの排他アクセス機能を利用することで、アプリケーションの多重起動を阻止できる。 Mutexコンストラクタの二つ目の引き数(ここではApplication.ProductName)は適当な文字列に変えることが出来る。
+
--[[c# - GC.KeepAlive versus using - Stack Overflow:http://stackoverflow.com/questions/635640/gc-keepalive-versus-using]]
+
--[[GC と Mutex - しばやん雑記:http://shiba-yan.hatenablog.jp/entry/20080323/1206200110]]
+

          

programming/mono/tips/index.wiki.txt

current previous
61,48 61,6
 

        

        
 
参考:[[FAQ: Technical - Mono, How to detect the execution platform ?:http://mono-project.com/FAQ:_Technical]]
参考:[[FAQ: Technical - Mono, How to detect the execution platform ?:http://mono-project.com/FAQ:_Technical]]
 

        

        
+

          
+
*名前付きミューテックスを使用する [#MONO_ENABLE_SHM]
+
Linux上で動作するMonoでは、[[名前付きミューテックス>programming/netfx/fcl/System.Threading.Mutex]]などプロセス間で共有されるハンドルをエミュレートするために共有メモリを使用している。 [[2.8以降のMonoではデフォルトで共有ハンドルの使用が無効化されている>http://www.mono-project.com/Release_Notes_Mono_2.8#Shared_handles_are_disabled_by_default]]。 有効にするには環境変数MONO_ENABLE_SHMをセットする。
+

          
+
#code(cs,名前付きミューテックスを使用する例){{
+
using System;
+
using System.Threading;
+

          
+
class Sample
+
{
+
  public static void Main()
+
  {
+
    bool createdNew;
+

          
+
    using (var mutex = new Mutex(false, "my-named-mutex", out createdNew)) {
+
      Console.WriteLine("createdNew: {0}", createdNew);
+
      Thread.Sleep(1000);
+
    }
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
$ mcs test.cs && for i in `seq 1 5` ; do mono test.exe & done 
+
createdNew: True
+
createdNew: True
+
createdNew: True
+
createdNew: True
+
createdNew: True
+

          
+
$ mcs test.cs && for i in `seq 1 5` ; do MONO_ENABLE_SHM=1 mono test.exe & done 
+
createdNew: True
+
createdNew: False
+
createdNew: False
+
createdNew: False
+
createdNew: False
+

          
+
}}
+

          
+
MONO_ENABLE_SHMが設定されているか(共有ハンドルを使用できるか)どうかを実行時に知る方法は、&msdn(netfx,member,System.Environment.GetEnvironmentVariable){Environment.GetEnvironmentVariable};を使う以外には特に用意されていない模様。
+

          
+

          
 
*gdiplus.dllでSystem.DllNotFoundExceptionとなる
*gdiplus.dllでSystem.DllNotFoundExceptionとなる
 
Fedora 8で起きた問題。 System.Windows.Forms等を使ったアプリケーションを起動するとgdiplus.dllが原因でSystem.DllNotFoundExceptionが発生する。 これは$LD_LIBRARY_PATH(yumでインストールした場合は/usr/lib)にlibgdiplus.soが存在せず、動的リンクに失敗することが原因で起きる。
Fedora 8で起きた問題。 System.Windows.Forms等を使ったアプリケーションを起動するとgdiplus.dllが原因でSystem.DllNotFoundExceptionが発生する。 これは$LD_LIBRARY_PATH(yumでインストールした場合は/usr/lib)にlibgdiplus.soが存在せず、動的リンクに失敗することが原因で起きる。