2013-11-15 2 views
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 객체를 병렬 모드로 가져 오는 작업을 수행해야합니다.

  • 사전에 db가 모든 Ids/Service 객체를 포함하고 있지만, 필자의 이해는 Singleton 클래스의 병렬 모드에서 처리 할 때 몇 가지 객체 이 문제로 이끄는 것을 건너 뜁니다.

  • 제 생각에는 전달 된 서비스 ID와 생성 된 서비스 개체가 로컬 스레드이므로 스레드가 문제가되지 않아야합니다. 이런 종류의 문제는 주어진 메소드 호출에 대해 하나의 쓰레드가 다른 쓰레드의 서비스 id 값을 쓰레드로 대체 할 때만 가능하다. 따라서 서비스 객체는 결국 서비스 객체로 끝나기 때문에 건너 뛰기는 거의 발생하지 않는다. 제대로

  • 은 현재 내가

검토하십시오 foreach 루프를 사용하는 대신 Parallel.ForEach/Parallel.Invoke에 의해 비 스레드 모드에서 동일한 코드를 실행할 수 있어요이 경우 멀티 스레딩을 이해하지 문제를 해결하는 데 도움이되는 귀하의 견해 또는 모든 포인터를 알려주십시오.

답변

0
나의 이해에서 6,

서비스 ID가 전달 및 서비스 객체는 스레드에 로컬 만들어

이해는 두 개의 스레드가 동일한 서비스 ID를 요청하는 경우 두 개의 스레드가 모두 같은 단일 개체에서 작업을 할 것, 잘못된 것입니다 . 별도의 객체를 원한다면 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에 고정되어야합니다. (아마도 System.Collections.Concurrent 네임 스페이스 스레드 안전 컬렉션에서 InitLanes 변경) 잠금보다 더 나은 솔루션이 될 것이라고

그러나 서비스는 사용자의 통제하에있는 경우 당신은 어떻게 든 service.InitLanes.Add 안전 스레드로 수정할 수 있습니다.

+0

감사 스콧에게, 두 개 이상의 스레드가 점점 실현하지 않았다 InitLanes는 사전에 정의 된 동일한 서비스 객체를 사용하므로 문제가 발생합니다. InitLanes는 비표준 모드에서 광범위하게 액세스되는 사용자 정의 데이터 구조이며, 스레드 안전하지 않은 목록으로 변환하는 것이 현명합니다. 현재 제안 된대로 lock 객체를 사용하고 있습니다. –

+0

'InitLanes'은 클래스를 통과해야만 내부 상태가 엉망이되지 않도록 적절한 잠금을 수행해야하는 맞춤 데이터 구조입니다. ['ReaderWriterLock'] (http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock%28v=vs.110%29.aspx)이 변환을 수행하는 데 큰 도움이 될 것입니다. 많은 독자가 동시에 모든 클래스에 액세스 할 수 있지만 한 명의 작가가있는 즉시 액세스를 차단하여 자물쇠가 잠금 장치가있는 위치가 아닌 다른 위치에 독자 또는 작성자가 액세스 할 수 없도록합니다 . –

1

1. 싱글 톤 구현은 항상 다중 스레드 방식으로이를 사용하는 것에 대해 생각합니다. 항상 멀티 스레드 싱글 톤 패턴 변형을 사용하십시오. 그 중 하나 인 게으른 싱글 톤입니다.

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; 
     } 
    } 
} 

읽기에 대한 그 here

병렬 루프 본문의 서비스 변수의

구석 구석의 잠금 ING

// 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.Consider : 게으른 싱글 적절한 LazyThreadSafeMode consturctor 인수 System.Lazy를 사용하여 사용 여기에 멀티 스레딩을 사용하십시오. 잠금 코드를 사용하면 동기 코드만큼 코드가 복잡해지지 않습니다. 그래서 반드시 paralelised/멀티 스레드 코드를 만드는 것은 당신의 장점

대신 재발 휠의 적절한 동시 컬렉션 4.Use

제공 - 그것은 참으로 문제이고, System.Collections.Concurrent Namespace

관련 문제