2011-12-05 3 views
7

내 질문은 아래 코드에서 인스턴스 메소드가 내가 생각하는 변수에 액세스 할 수 있는지 또는 여전히 작동하는 동안 다른 스레드에 의해 변경 될 수 있는지 확인할 수 있습니까? ? 폐쇄와 관련이 있습니까? 즉, 열거 형은 안전하므로 IEnumerable<T>의 로컬 복사본에서 작업 할 예정입니까?멀티 스레딩, lambdas 및 로컬 변수

내 질문을 의역으로 말하면, 공유 변수를 작성하지 않으면 잠금이 필요합니까?

public class CustomerClass 
{ 
    private Config cfg = (Config)ConfigurationManager.GetSection("Customer"); 

    public void Run() 
    { 
     var serviceGroups = this.cfg.ServiceDeskGroups.Select(n => n.Group).ToList(); 

     var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID")); 
     Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
      groupedData, 
      () => new CustomerDataContext(), 
      (g, _, ctx) => 
      { 
       var inter = this.FindOrCreateInteraction(ctx, g.Key); 

       inter.ID = g.Key; 
       inter.Title = g.First().Field<string>("Title"); 

       this.CalculateSomeProperty(ref inter, serviceGroups); 

       return ctx; 
      }, 
      ctx => ctx.SubmitAllChanges()); 
    } 

    private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID) 
    { 
     var inter = ctx.Interactions.Where(n => n.Id = ID).SingleOrDefault(); 

     if (inter == null) 
     { 
      inter = new Interaction(); 
      ctx.InsertOnSubmit(inter); 
     } 

     return inter; 
    } 

    private void CalculateSomeProperty(ref Interaction inter, IEnumerable<string> serviceDeskGroups) 
    { 
     // Reads from the List<T> class instance variable. Changes the state of the ref'd object. 
     if (serviceGroups.Contains(inter.Group)) 
     { 
      inter.Ours = true; 
     } 
    } 
} 
+0

CustomerDataContext 구현을 공유 할 수 있습니까? 이것은 데이터 경쟁이 현재 발생할 수있는 유일한 장소 인 것처럼 보입니다. –

+0

CustomerDataContext는 표준 Entity Framework DataContext이며 실제로는 스레드 로컬이므로 아무 데이터도 존재하지 않습니다. –

답변

3

나는 답을 찾았으며 그 과정에서 또한 질문이있는 것으로 보인다.

진짜 질문은 실제 개체로 판명 된 로컬 "변수"가 동시 액세스에 대해 신뢰할 수 있는지 여부입니다. 대답은 '아니오'입니다. 스레드가 안전한 방식으로 처리되지 않는 내부 상태가 발생하면 모든 베팅이 해제됩니다. 클로저는 도움이되지 않는다. 단지 객체에 대한 참조를 캡처한다.

내 특정 경우

- foreach를 호출 할 때마다, Contains(), Where() 등 신선한 새로운 IEnumerator를 얻을 수 있기 때문에 동시는에서만 볼 수있는, 그것은 실제로 스레드 안전하다, IEnumerable<T> 읽하지 않고 그것을 기록 그것을 요구 한 thread 그러나 다른 모든 개체도 하나씩 확인해야합니다.

그래서, 만세, 나에 대한 잠금 또는 동기화 된 컬렉션 :

덕분에 @ebb 없습니다 당신이 직접 질문에 대답하지 않았지만 @ 데이브는, 당신은 올바른 방향으로 절 지적했다. 당신은 결과에 관심이 있다면


,이 행의 처리 시간을 시뮬레이션 할 수 Thread.SpinWait 내 홈 PC (쿼드 코어)에서 실행됩니다. 실제 응용 프로그램은 로컬 네트워크에 SQL Server가있는 듀얼 코어 하이퍼 스레드 컴퓨터에서 거의 2 배 (01:03 대 00:34) 향상되었습니다.

Singlethreaded 단일 스레드, foreach을 사용합니다. 나는 이유를 모르지만 꽤 많은 수의 크로스 코어 컨텍스트 스위치가 있습니다.

Multithreaded Parallel.ForEach을 사용하면 필요한 경우 스레드 로컬과 잠금을 해제 할 수 있습니다.

1

지금, 내가 말할 수있는, 인스턴스 방법은 어떤 멤버 변수를 사용하지 않는에서. 그것은 그것들을 stateless하게 만들고 따라서 threadsafe합니다. 그러나 동일한 경우 코드 명확성과 약간의 성능 향상을 위해 "정적"으로 표시하는 것이 좋습니다.

이러한 인스턴스 메서드가 멤버 변수를 사용하는 경우 해당 변수와 마찬가지로 스레드 안전성이 있습니다 (예를 들어 간단한 목록을 사용하는 경우 스레드가 안전하지 않으며 이상한 동작이 나타날 수 있음). 간단히 말하자면, 멤버 변수는 쉬운 스레드 안전성의 적입니다.

내 리팩터 (면책 조항, 테스트하지 않음)입니다. 전달 된 데이터를 제공하려는 경우 파라미터로 전달하고 멤버 변수로 전달하면 saner로 유지됩니다.

업데이트 : 읽기 전용 목록을 참조 할 수있는 방법을 요청 했으므로 인스턴스 변수를 공유 할 수 있도록 정적 태그를 추가하고 제거했습니다.

public class CustomerClass 
{ 

private List<string> someReadOnlyList; 

    public CustomerClass(){ 
     List<string> tempList = new List<string>() { "string1", "string2" }; 
     someReadOnlyList = ArrayList.Synchronized(tempList); 
    } 

    public void Run() 
    { 
     var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID")); 

     Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
      groupedData, 
      () => new CustomerDataContext(), 
      (g, _, ctx) => 
      { 
       var inter = FindOrCreateInteraction(ctx, g.Key); 

       inter.ID = g.Key; 
       inter.Title = g.First().Field<string>("Title"); 

       CalculateSomeProperty(ref inter); 

       return ctx; 
      }, 
      ctx => ctx.SubmitAllChanges()); 
    } 

    private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID) 
    { 
     var query = ctx.Interactions.Where(n => n.Id = ID); 

     if (query.Any()) 
     { 
      return query.Single(); 
     } 
     else 
     { 
      var inter = new Interaction(); 
      ctx.InsertOnSubmit(inter); 
      return inter; 
     } 
    } 

    private void CalculateSomeProperty(ref Interaction inter) 
    { 
     Console.Writeline(someReadOnlyList[0]); 
     //do some other stuff 
    } 
} 
+0

답장을 보내 주셔서 감사합니다 :) 그러나, 그 목록에 글을 쓰지 않고 독서 만한다면? 두 번째 방법 인'CalculateSomeProperty()'는'List '에서 몇 가지를 보았으나 결코 추가하거나 제거하지 않습니다. 다른 클래스가 필요하기 때문에 전체 클래스에서 쉽게 볼 수 있습니다. –

+0

@VladislavZorov,'ReadOnlyCollection' - http://msdn.microsoft.com/en-us/library/ms132474.aspx – ebb

+0

그래서'List '(또는'ReadOnlyCollection ')을 읽는 것조차도 스레드 기반의 것이 아니며, 안전한 작동. 그러나 "공용 static (Visual Basic의 Shared) 멤버는 스레드로부터 안전합니다"라는 문장을 이해할 수 없습니까? 왜? –