2009-08-17 3 views
16

C#アプリケーションで「ボックス化」オブジェクトをロックしようとしていますが、これは不可能ですか?C#スレッド - ロックオブジェクト

 Thread b1 = new Thread(new ThreadStart(t1.w)); 
     b1.Start(); 
     Thread b2 = new Thread(new ThreadStart(t1.w)); 
     b2.Start(); 

部分がロックされていないが:別のクラスにおいて

class t 
    { 
     System.Object t_x = new object(); 

     public t(int p) 
     { 
      t_x = p; 

     } 

     public void w() 
     { 
      lock (t_x) 
      { 
       for (int i = 0; i < 4; i++) 
       { 

        { 
         t_x = ((int)t_x) + 1; 
         Console.WriteLine(t_x); 
         Thread.Sleep(1000); 
        } 
       } 
      } 
     } 
    } 

私は2つのスレッドを開始することができます。 任意のオブジェクト(オブジェクトとして作成され、オブジェクトとして変更されていないオブジェクト)をロックすると、ロックされます。 ボクシング操作はどういうわけか "depromotes"私のオブジェクトですか?

+2

なぜあなたはロックブロック内のロックオブジェクトを再割り当てしますか?それは目的を打ち負かします - 同期の全体のポイントは、diffスレッドが "共有"リソースのために互いを踏み合わないようにすることです。 – Gishu

+0

私は答えが良いものではないと受け入れたと思っています –

+0

経験の浅い試した.NETスレッドの中には、ロックステートメントがロックを実際に「ロック」したり、オブジェクトを保護するという誤った繰り返しがあるようです。オブジェクトは、コードのロックされたブロック、つまりクリティカルセクションのキーとなるという事実を見逃しているようです。 –

答えて

16

別個のロックオブジェクトを作成する必要があります。問題は、ループ内でt_xを再割り当てすることです。 b2がlockステートメントに到達する前にスレッドb1がループ内に入ると仮定すると、その時点でt_xはロックされていない新しいオブジェクトになるため、b2はlockステートメント内で許可されます。

+0

私はそれを行うより良い方法だと思う。 –

+4

@ケドリック - リーの答えをもっと慎重に読んでください。 'lock'文は' Monitor.Enter'と 'Monitor.Exit'を使うのとまったく同じです。これは、同じパターンのちょっとした構文です。問題は、 'lock'か' Monitor'を使うかどうかではありません。問題は、常に同じオブジェクトを渡してロックとして機能させることです。これは、アクセスするたびに別の方法でボックス化された値型を使用すると壊れます。 –

23

いいえ、あなたはこれを行うことはできません - ロックブロックは、以下の省略形です:

try(Monitor.Enter(lockObject)) 
{ 
    //critical section 
} 
finally 
{ 
    Monitor.Exit(lockObject) 
} 

documentation for Monitor.Enter状態、「使用モニターのオブジェクト(つまり、参照型である)、ではない値の型をロックします値型変数をEnterに渡すと、オブジェクトとしてボックス化されます。同じ変数をEnterに再度渡すと、別のオブジェクトとして囲まれ、スレッドはブロックしません。 "

+0

それを行うには良い方法。それは、マイクロソフト認定の最初の試験70-536にあります。 –

+7

これは正解です。コードスニペット( 'lock'ステートメントは問題ありません)ではなく、値型と参照型の違いが記述されているためです。値型は、汎用の 'object'基本クラスに変換されるたびに再びボックス化されるので、確実にロック対象として動作することはできません。これは受け入れられた答えよりも優れています。 –

2

ロックに余分なオブジェクトを使用する

object lockObj = new object(); 
public void foo() 
{ 
    lock(lockObj) 
    { 
    //do stuff here 
    } 
} 
2

lock(t_x)呼び出しは、一時オブジェクトとして整数を格納します。 lock(t_x)を呼び出すたびにNewオブジェクトが作成され、ロックは役に立たなくなります。

(ロックオブジェクトを期待整数から新しい一時的なオブジェクトを作成)

だけFemarefによって上記述べたような別々のロックオブジェクトを作成します。あなたが本当にしたい場合は(?必要)

0

オブジェクトをロックするには、ラッパーの種類使用することができます。

public class ObjectWrapper 
{ 
    public Object Value { get;set; } 
} 
+0

私はこのようにすることをお勧めしませんが(ロックするために別の 'オブジェクト'を使用するだけです)、私は正しい*であるので、下線を理解しません。 –

0

の場合:

public class IntWrapper 
{ 
    public int Value{get;set;} 
} 

をそれとも、もっと抽象的に滞在する必要がある場合データがロードされた時点を認識したい場合や、その前にデータを使用しようとする場合は、次のようにすることができます。

言い換えればブール値のフラグがありますが、クロススレッドの競合状態を防ぐためにアクセスします。

ユーザーがデータを使用しようとすると、データがロードされていない場合(変数をチェックする)、別のイベントハンドラをワーカーRunWorkerCompletedイベントに追加すると、データのロード時にユーザーが望むことがすぐに実行されます。

例:

public class MyClass 
{ 
    private bool dataIsReady = false; 
    private object locker = new object(); 
    BackgroundWorker worker; 

    public void Begin() 
    { 
     worker = new BackgroundWorker(); 
     worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    } 

    public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     lock (locker) 
     { 
      dataIsReady = true; 
     } 
    } 

    public void UseTriesToUseData() 
    { 
     lock (locker) 
     { 
      if (dataIsReady) 
      { 
       DoStuff(); 
      } 
      else 
      { 
       this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoStuffCaller); 
      } 
     } 
    } 

    private void DoStuff() 
    { 
     // Do stuff with data. 
    } 

    private void DoStuffCaller(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.DoStuff(); 
    } 
}