2009-11-21 27 views
3

好吧,玩弄System.Threading.Tasks中的.NET 4.0 Parellel擴展。我發現似乎很奇怪的行爲,但我認爲我只是做錯了什麼。我有一個接口和一對夫婦執行clases,他們很簡單。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.這是好的,我看到它在TE調試因爲這兩種不同的類型會有所不同。然而,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)); 
} 

請注意,我們只是把副本內循環,並捕捉該副本,讓每個時間變量的不同「實例」 。

或者,而不是捕捉階段,我們可以只捕捉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)); 
} 

如果沒有幫助,您可以發佈一個簡短而完整的程序,它演示了此問題?這將使追蹤更容易。

+0

我雖然同樣的事情,「這與p無關」,這就是爲什麼我沒有想到foreach問題。但是,我添加了「舞臺複製」,問題被清除了。有趣。將做更多的調查...... – MikeD 2009-11-21 07:05:08

+0

是的,我在循環中聲明p。 – MikeD 2009-11-21 07:06:58