2009-11-21 3 views
3

Ok. System.Threading.Tasks에서 .Net 4.0 Parellel Extensions를 사용하여 주변에서 놀고 있습니다. 나는 별난 behaivor와 같게 보이는 것을 발견하고있다. 그러나 나는 틀린 무엇인가하고있는 you''re를 추측한다. 인터페이스와 클레임을 구현하는 커플이 있는데, 이것들은 간단합니다.C# 병렬 확장 Task.Factory.StartNew가 잘못된 개체에서 메서드를 호출합니다.

interface IParallelPipe 
{ 
    void Process(ref BlockingCollection<Stream> stream, long stageId); 
} 

class A:IParallelPipe 
{ 
    public void Process(ref BlockingCollection<Stream> stream, long stageId) 
    { 
     //do stuff 
    } 
} 

class B:IParallelPipe 
{ 
    public void Process(ref BlockingCollection<Stream> stream, long stageId) 
    { 
     //do stuff 
    } 
} 

그런 다음 내 수업을 시작합니다. 이것은 문제가 발생하는 곳입니다. 필자는 전달 된 클래스에서 호출 할 클래스를 구현하는 것에 대한 정보를 얻은 다음 팩토리를 호출하여 인스턴스를 생성 한 다음 해당 인스턴스로 작업을 만들고 시작합니다. 여기에 표시된 내 샘플이 각각의 실행에서

BlockingCollection<Stream> bcs = new BlockingCollection<Stream>();     
foreach (Stage s in pipeline.Stages) 
{ 
    IParallelPipe p = (IParallelPipe)Factory.GetPipe(s.type); 
    Task.Factory.StartNew(() => p.Process(ref bcs, s.id)); 
} 

, pipeline.Stages은 두 가지 요소, 클래스 A로 인스턴스화됩니다 하나 이것은, 내가 테 디버거에서 그것을 잘 볼 수있다 클래스 B와 다른 포함 p가 두 가지 유형으로 나옵니다. 그러나 클래스 B는 호출되지 않고 대신 A.Process (...) 메서드를 두 번 호출합니다. 둘 다 전달 된 stageId를 포함합니다 (즉, 두 개의 호출이 서로 다른 stageIds를 가짐). 나는 조금을 별도의 것들을 밖으로 경우

지금 바로 테스트를 위해 나는 일이 이런 식으로 뭔가를 수행하여 작동시킬 수 있습니다

BlockingCollection<Stream> bcs = new BlockingCollection<Stream>();     
A a = null; 
B b = null; 
foreach (Stage s in pipeline.Stages) 
{ 
    IParallelPipe p = (IParallelPipe)Factory.GetPipe(s.type); 
    if(p is A) 
     a = p; 
    else 
     b = p; 
} 
Task.Factory.StartNew(() => a.Process(ref bcs, idThatINeed)); 
Task.Factory.StartNew(() => b.Process(ref bcs, idThatINeed)); 

이 해당 클래스를 호출!

모든 의견 ???

답변

4

당신이 묘사하는 동작은 이상한 것처럼 보입니다. 올바른 인스턴스를 사용할 수는 있지만 잠재적으로 잘못된 스테이지 ID 인 old foreach variable capture 문제가 발생할 것으로 예상됩니다. 변수 s이 캡쳐되고 작업 팩토리가 클로저를 평가할 때 s 값이 변경되었습니다.

이것은 코드에서 분명히 문제가되지만 문제가 발생하는 이유를 설명하지 않습니다. 그냥 확인하기 위해, 당신은 정말로 p을 외부에 있지 않고이라고 선언하고 있습니까? 루프 밖에서 p을 선언하면 모든 것을 설명 할 수 있습니다. 우리가 루프 내부 사본을 복용하고, 그 사본을 캡처하고

BlockingCollection<Stream> bcs = new BlockingCollection<Stream>(); 
foreach (Stage s in pipeline.Stages) 
{ 
    Stage copy = s; 
    IParallelPipe p = (IParallelPipe)Factory.GetPipe(s.type); 
    Task.Factory.StartNew(() => p.Process(ref bcs, copy.id)); 
} 

주, 변수마다 다른 "예"를 얻을 : 여기

하지만 캡처 문제에 대한 수정 프로그램입니다 .

BlockingCollection<Stream> bcs = new BlockingCollection<Stream>(); 
foreach (Stage s in pipeline.Stages) 
{ 
    long id = s.id; 
    IParallelPipe p = (IParallelPipe)Factory.GetPipe(s.type); 
    Task.Factory.StartNew(() => p.Process(ref bcs, id)); 
} 

이 도움이되지 않는 경우이 문제를 보여주는 짧지 만 완전한 프로그램을 게시 할 수 : 대신 무대를 캡처의 우리가 필요로 그게 다야로

또한, 우리는 단지 ID를 캡처 할 수 있습니다 ? 그러면 추적하기가 훨씬 쉬워집니다.

+0

나는 "이것은 p와 아무런 관련이 없습니다."라고했는데, 이것이 내가 foreach 문제를 생각하지 않은 이유입니다. 그러나 "무대 복사"를 추가하고 문제가 해결되었습니다. 흥미 롭 군. 더 많은 조사를 할 것입니다 ... – MikeD

+0

예, 루프 내에서 p를 선언하고 있습니다. – MikeD