3

コードの詳細:

// Singleton class CollectionObject

public class CollectionObject
{
    private static CollectionObject instance = null;        

// GetInstance() is not called from multiple threads
    public static CollectionObject GetInstance()
    {
        if (CollectionObject.instance == null)
            CollectionObject.instance = new CollectionObject();

        return CollectionObject.instance;
    }

// Dictionary object contains Service ID (int) as key and Service object as the value
// Dictionary is filled up during initiation, before the method call ReadServiceMatrix detailed underneath

    public Dictionary<int, Service> serviceCollectionDictionary = new Dictionary<int,Service>();

    public Service GetServiceByIDFromDictionary(int servID)
    {
        if (this.serviceCollectionDictionary.ContainsKey(servID))
            return this.serviceCollectionDictionary[servID];
        else
            return null;
    }

}

 DataTable serviceMatrix = new DataTable();

 // Fill serviceMatrix data table from the database

 private int ReadServiceMatrix()
 {
    // Access the Singleton class object
    CollectionObject collectionObject = CollectionObject.GetInstance();

    // Parallel processing of the data table rows
    Parallel.ForEach<DataRow>(serviceMatrix.AsEnumerable(), row =>
    {
       //Access Service ID from the Data table
       string servIDStr = row["ServID"].ToString().Trim();

       // Access other column details for each row of the data table

       string currLocIDStr = row["CurrLocId"].ToString().Trim();
       string CurrLocLoadFlagStr = row["CurrLocLoadFlag"].ToString().Trim();
       string nextLocIDStr = row["NextLocId"].ToString().Trim();
       string nextLocBreakFlagStr = row["NextLocBreakFlag"].ToString().Trim();
       string seqStr = row["Seq"].ToString().Trim();

       int servID = Int32.Parse(servIDStr);
       int currLocID = Int32.Parse(currLocIDStr);
       int nextLocID = Int32.Parse(nextLocIDStr);
       bool nextLocBreakFlag = Int32.Parse(nextLocBreakFlagStr) > 0 ? true : false;
       bool currLocBreakFlag = Int32.Parse(CurrLocLoadFlagStr) > 0 ? true : false;
       int seq = Int32.Parse(seqStr);

       // Method call leading to the issue (definition in Collection Object class)
       // Fetch service object using the Service ID from the DB                      

       Service service = collectionObject.GetServiceByIDFromDictionary(servID);

       // Call a Service class method

       service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));

    }

発生する問題は次のとおりです。

  • ディクショナリ内のすべての Service オブジェクトに対する上記のコードでは、後続のメソッド呼び出しが行われないため、以降の処理で問題が発生します。並列モードで辞書から Service オブジェクトをフェッチする必要があります

  • 辞書にはすべての Ids /Service オブジェクトが含まれていますが、Singleton クラスの Parallel モードで処理する場合、いくつかのオブジェクトがスキップされて問題が発生することを理解しています。

  • 私の理解では、渡されたサービス ID と作成されたサービス オブジェクトはスレッドに対してローカルであるため、直面している問題はありません。この種の問題は、特定のメソッド呼び出しに対して、あるスレッドが別のスレッドのサービス ID 値をそのスレッドで置き換える場合にのみ可能です。したがって、両方が Service オブジェクトになり、スキップされるものはほとんどありません。この場合のマルチスレッドを正しく理解していません

  • 現在、Parallel.ForEach / Parallel.Invoke の代わりに foreach ループを使用して、非スレッド モードで同じコードを実行できます。

確認して、問題の解決に役立つ見解や指針をお知らせください

4

2 に答える 2

1

1.シングルトンの実装は、マルチスレッドでの使用を常に考えてください。常にマルチスレッドのシングルトン パターン バリアントを使用します。そのうちの 1 つはレイジー シングルトンです。適切なLazyThreadSafeModeコンストラクター引数でSystem.Lazyを使用して Lazy シングルトンを使用します。

public class LazySingleton3
{
     // static holder for instance, need to use lambda to enter code here
     //construct since constructor private
     private static readonly Lazy<LazySingleton3> _instance
         = new Lazy<LazySingleton3>(() => new LazySingleton3(), 
                                          LazyThreadSafeMode.PublicationOnly);

     // private to prevent direct instantiation.
     private LazySingleton3()
     {
     }

     // accessor for instance
     public static LazySingleton3 Instance
     {
         get
         {
             return _instance.Value;
         }
     }
}

ここでそれについて読んでください

2.並列ループ本体でサービス変数のロックを使用する

// Method call leading to the issue (definition in Collection Object class)
// Fetch service object using the Service ID from the DB                      
Service service = collectionObject.GetServiceByIDFromDictionary(servID);

lock (service)
{    
    // Call a Service class method        
    service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID,
                          currLocBreakFlag, nextLoc.SequentialID,
                          nextLocBreakFlag, seq));
}

3.ここでマルチスレッドの使用を検討してください。ロック コードを使用すると、コードのパフォーマンスが同期ほど低くなります。したがって、マルチスレッド化/並列化されたコードが利点をもたらすことを確認してください

4.車輪を再発明する代わりに、適切な並行コレクションを使用する - System.Collections.Concurrent Namespace

于 2013-11-15T13:45:22.283 に答える
1

私の理解では、渡されたサービスIDと作成されたサービスオブジェクトはスレッドに対してローカルです

あなたの理解は間違っています。2 つのスレッドが同じサービス ID を要求した場合、2 つのスレッドは両方とも同じ単一のオブジェクトで動作します。個別のオブジェクトが必要な場合は、既存の値の辞書の代わりに何らかのnew Service()呼び出しを行う必要があります。GetServiceByIDFromDictionary

複数のスレッドが同じオブジェクトを使用している可能性があるため、スレッドセーフではない可能性が高いserviceという事実に問題があると思います。service.InitLanes.Add

最も簡単な修正は、その単一のステップをロックすることです

  //...SNIP...

  Service service = collectionObject.GetServiceByIDFromDictionary(servID);

  // Call a Service class method, only let one thread do it for this specific service instance, 
  // other threads locking on other instances will not block, only other threads using the same instance will block
  lock(service)
  {
      service.InitLanes.Add(new Service.LaneNode(currLoc.SequentialID, currLocBreakFlag, nextLoc.SequentialID, nextLocBreakFlag, seq));
  }

}

これは、これが同時に使用されるParallel.Foreach唯一の場所であることを前提としていcollectionObject.GetServiceByIDFromDictionaryます。そうでない場合は、返されたサービスのメソッドを呼び出す可能性のある他の場所もロックする必要がありますservice

ただし、Service が制御下にあり、なんらかのservice.InitLanes.Add方法でスレッド セーフに変更できる場合 (おそらく名前空間InitLanesからのスレッド セーフ コレクションに変更する場合)、ロックよりも優れたソリューションになります。System.Collections.Concurrent

于 2013-11-15T08:53:45.313 に答える