2012-04-10 8 views
1

たとえば、マルチスレッドのシナリオで使用されるクラスのリストがあるとします。スレッドセーフな方法でリストを列挙する

public class MyClass 
{ 
    List<MyItem> _list= new List<MyItem>(); 
    protected object SyncRoot { 
     get { 
     return ((IList)_list).SyncRoot; 
     } 
    } 

    public void Execute1() 
    { 
     lock(SyncRoot) 
     { 
      foreach(var item in _list) DoSomething(item); 
     } 
    } 

    public void Execute2() 
    { 
     Item[] list; 
     lock(SyncRoot) 
     { 
      list=_list.ToArray(); 
     } 
      for(var i=0;i<list.Length;i++) DoSomething(list[i]); 
     } 
} 

方法EXECUTE1はスレッドセーフな方法でリストを列挙するために「正常」な方法です。しかし、何についてExecute2?このアプローチはまだスレッドセーフですか?

+1

MyItemsが参照型(オブジェクト)である場合、リストと配列の両方が同じオブジェクトを最終的に指しているため、Execute2は安全ではありません。 – Will

+0

@ウィルこの特定のシナリオでは問題はありません。私はリスト自体についてのみ関心を持っています。必要であれば、オブジェクトは同期自体を処理できます。 – MikeSW

+0

@HenkHolterman私はどのオブジェクトも使用できることは知っていますが、リストの同期オブジェクトを使用する方が良いかどうか疑問です。私はそれがなぜ公開されているのか、という意味ですか? – MikeSW

答えて

1

_listの1回おきの使用が同じlockステートメントで保護されている限り、安全です。リストに排他的にアクセスし、その内容をコピーしてコピーを処理しています(これには、有効範囲のために排他的アクセス権があります)。一見して少し無駄ですが、特定の状況下では正当なアプローチです。

+0

はい、リストから項目を追加/削除するには、同じロックを使用します。しかし、私はローカルリストが自動的に排他アクセスを取得する理由を理解していません。 – MikeSW

+0

@MikeSW:メソッド 'Execute2'を除いて、他の人にとっては範囲外です。 – Jon

2

リストのコピーへのアクセスは、両方のシナリオでスレッドセーフです。もちろん、MyItem要素はどのような方法でも同期されません。

2番目のフォームはもう少し高額に見えますが、DoSomething()が実行されている間に元のAdd/Removeを許可します。配列は一種のスナップショットのように機能し、必要に応じて役立ちます。 ToList()を使用することもできます。

関連する問題