- やまと
- 会議室デビュー日: 2003/08/21
- 投稿数: 11
|
投稿日時: 2004-05-14 11:29
お教え下さい。
以下のようなコードがあったとします。
| コード: |
| Imports System.Threading
Public Class MutexTest
Private Shared m_MutexTest As MutexTest
Public Shared Sub Main()
m_MutexTest = New MutexTest
Application.Run()
End Sub
Private Sub New()
Threading.ThreadPool.QueueUserWorkItem(AddressOf th1, Nothing)
Threading.ThreadPool.QueueUserWorkItem(AddressOf th2, Nothing)
End Sub
Private Sub th1(ByVal state As Object)
Dim m As New Mutex(False, "TEST_MUTEX")
Dim startTime As DateTime = DateTime.Now
m.WaitOne()
Thread.Sleep(1000)
Console.WriteLine("th1_待機時間 : " & DateTime.Now.Subtract(startTime).TotalSeconds)
End Sub
Private Sub th2(ByVal state As Object)
Dim m As New Mutex(False, "TEST_MUTEX")
Dim startTime As DateTime = DateTime.Now
m.WaitOne()
Thread.Sleep(1000)
Console.WriteLine("th2_待機時間 : " & DateTime.Now.Subtract(startTime).TotalSeconds)
End Sub
End Class
|
投稿用に検証的な部分のみを記述したコードなので、
実際にはこのようなmutexを解放しないコードを記述することはありませんが、
このコードに予期している動作は、
コンストラクタによってth1 メソッドと th2 メソッドをスレッドプールに置き、
先に動作したスレッドの "th*_待機時間 : 秒数" が表示される、というものです。
MSDNの WaitHandle.WaitOne メソッド(引数無し)のヘルプには、
| 引用: |
| このメソッドの呼び出し元は、現在のインスタンスがシグナルを受け取るまでは、
無期限にブロックします。
|
とあるので、先に動作したスレッドが mutex を解放するコードを記述しない限り、
後に動作したスレッドは、waitOne部分で無期限にブロックされるはずです。
しかし、実際にこのコードを実行してみると、
40秒後に、後に動作したスレッドによって、
"th2_待機時間 : 41.509688"
と表示されてしまいます。
これはなぜこの様な動作になってしまうのでしょう?
なお、動作環境は、CLR バージョン 1.1.4322.573 で、
会社と自宅の WindowsServer2003 と Windows2000Professional の
両方で、同現象を確認しました。
|
- なちゃ
- ぬし
- 会議室デビュー日: 2003/06/11
- 投稿数: 872
|
投稿日時: 2004-05-14 11:33
| 引用: |
|
やまとさんの書き込み (2004-05-14 11:29) より:
とあるので、先に動作したスレッドが mutex を解放するコードを記述しない限り、
後に動作したスレッドは、waitOne部分で無期限にブロックされるはずです。
しかし、実際にこのコードを実行してみると、
40秒後に、後に動作したスレッドによって、
"th2_待機時間 : 41.509688"
と表示されてしまいます。
これはなぜこの様な動作になってしまうのでしょう?
|
かならず決まったような時間ですか?
単純にFinalizeによって自動解放されたのでは?って気もしますけど…
|
- 甕星
- ぬし
- 会議室デビュー日: 2003/03/07
- 投稿数: 1185
- お住まい・勤務地: 湖の見える丘の上
|
投稿日時: 2004-05-14 12:45
| 引用: |
|
MSDNの WaitHandle.WaitOne メソッド(引数無し)のヘルプには、
このメソッドの呼び出し元は、現在のインスタンスがシグナルを受け取るまでは、
無期限にブロックします。
|
とあるので、先に動作したスレッドが mutex を解放するコードを記述しない限り、
後に動作したスレッドは、waitOne部分で無期限にブロックされるはずです。
[/quote]
貴方の書いたコードだと、mutexをスタック変数にしていますよね。当然スコープを外れたあと、いずれガベージコレクタに回収されて、mutexも開放されるわけですよ。
| 引用: |
|
しかし、実際にこのコードを実行してみると、
40秒後に、後に動作したスレッドによって、
"th2_待機時間 : 41.509688"
と表示されてしまいます。
これはなぜこの様な動作になってしまうのでしょう?
|
なので、たまたま40秒後にガベージコレクタが働いてmutexが開放されただけでは?
_________________ 甕星 <mikahosi@abox9.so-net.ne.jp>
http://blogs.msmvp.jp/mikahosi/
|
- やまと
- 会議室デビュー日: 2003/08/21
- 投稿数: 11
|
投稿日時: 2004-05-14 13:11
なちゃ様、甕星様ご回答頂きありがとうございます。
ご指摘の通り、例えば th1 スレッドの末尾にThread.Sleep メソッドを入れておくと、
指定した秒数が40秒に、さらに加算されました。
そこで、各メソッドにスタック変数としていた m を
インスタンス変数とするべく、以下のように書き換えました。
| コード: |
|
Private m_MTX1 As Mutex
Private m_MTX2 As Mutex
Private Sub th1(ByVal state As Object)
m_MTX1 = New Mutex(False, "TEST_MUTEX")
Dim startTime As DateTime = DateTime.Now
m_MTX1.WaitOne()
Thread.Sleep(1000)
Console.WriteLine("th1_待機時間 : " & DateTime.Now.Subtract(startTime).TotalSeconds)
End Sub
'th2も同様に書き換える
|
しかしながら、やはり、同様の問題が発生してしまいました。
メソッド内でインスタンス変数に対しmutexのインスタンスを代入しても、
そのメソッドの動作が終了しスコープから外れると、
インスタンス変数に対してもFinalizeが呼び出されてしまうのでしょうか?
どうかお教え下さい。
|
- やまと
- 会議室デビュー日: 2003/08/21
- 投稿数: 11
|
投稿日時: 2004-05-14 14:15
上記の事を検証するために、以下のコードを書いてみました。
| コード: |
|
Imports System.Threading
Public Class ScopeTester
Private Shared m_ScopeTester As ScopeTester
Public Shared Sub Main()
m_ScopeTester = New ScopeTester
Application.Run()
End Sub
Private Sub New()
Threading.ThreadPool.QueueUserWorkItem(AddressOf th1, Nothing)
Threading.ThreadPool.QueueUserWorkItem(AddressOf th2, Nothing)
End Sub
Private m_tc1 As TestClass
Private m_tc2 As TestClass
Private Sub th1(ByVal state As Object)
m_tc1 = New TestClass
End Sub
Private Sub th2(ByVal state As Object)
m_tc2 = New TestClass
End Sub
End Class
Public Class TestClass
Public Sub New()
Console.WriteLine(".ctor : " & Thread.CurrentThread.GetHashCode)
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
Console.WriteLine("Finalize : " & Thread.CurrentThread.GetHashCode)
End Sub
End Class
|
上記コードを動作させると、各スレッドによるコンストラクタ呼び出しは
確認できるのですが、
40秒経過後、Finalize は呼ばれていないようなのです。
やはり、Mutex オブジェクトのみで起こる現象なのでしょうか?
|
- 一郎
- ぬし
- 会議室デビュー日: 2002/10/11
- 投稿数: 1081
|
投稿日時: 2004-05-15 21:21
Threading.ThreadPool.QueueUserWorkItem()ではなく、Thread.Start()でやってみたところ、40秒も待たずに片方のメソッドが終了するとすぐにリリースされました。
これはスレッド自身が、自分が所有しているMutexを(内部で)覚えていて終了するときにリリースしているのではないでしょうか。
QueueUserWorkItem()の場合はひとつのスレッドでキューに溜まっているメソッドを順番に呼び出すの(かな?)でしょうから、「40秒後にリリース」というのはThreadPoolクラスの機能かもしれませんね。
ちなみにGC.Collect()を呼んでみましたが、効果はありませんでした。
GCは関係ないみたいですね。
|
- やまと
- 会議室デビュー日: 2003/08/21
- 投稿数: 11
|
投稿日時: 2004-05-15 23:02
一郎様、ご回答頂きありがとうございます。
私自身でもいろいろ試行錯誤してみたところ、
どうやら、
サンプルコードでの、各th1 th2 メソッドの中の、
mutex インスタンス生成直後、
InitializeLifetimeService を呼び出すと、40秒でmutexが解放されず、
保持し続けるようです。
System.MarshalByRefObject.InitializeLifetimeService メソッド含め、
オブジェクト生存期間について、再勉強してみます。
お答えくださった皆様、大変ありがとうございました。
|