C#Parallel Extensions Task.Factory.StartNew在错误的对象上调用方法

C#Parallel Extensions Task.Factory.StartNew在错误的对象上调用方法,c#,parallel-processing,C#,Parallel Processing,好的,在System.Threading.Tasks中使用.Net 4.0并行扩展。我发现了一种奇怪的行为,但我想我是做错了什么。我有一个接口和几个实现类,它们很简单 interface IParallelPipe { void Process(ref BlockingCollection<Stream> stream, long stageId); } class A:IParallelPipe { public void Process(ref Blocking

好的,在System.Threading.Tasks中使用.Net 4.0并行扩展。我发现了一种奇怪的行为,但我想我是做错了什么。我有一个接口和几个实现类,它们很简单

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
    }
}
接口IParallelPipe
{
void进程(ref BlockingCollection stream,long stageId);
}
A类:IParallelPipe
{
公共作废流程(ref BlockingCollection stream,long stageId)
{
//做事
}
}
B类:IParallelPipe
{
公共作废流程(ref BlockingCollection stream,long stageId)
{
//做事
}
}
然后我让我的班级从这些开始。这就是问题所在。我基本上从传入的类型中获取要调用的实现类的信息,然后调用工厂对其进行实例化,然后用它创建一个任务并启动它。如图所示:

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)); 
}
BlockingCollection bcs=new BlockingCollection();
foreach(管道中的阶段s。阶段)
{
IParallelPipe p=(IParallelPipe)Factory.GetPipe(s.type);
Task.Factory.StartNew(()=>p.Process(ref-bcs,s.id));
}
在我的示例中的每次运行中,pipeline.Stages都包含两个元素,一个被实例化为类A,另一个被实例化为类B。这很好,我在te调试器中将其视为p,并附带了两种不同的类型。然而,从未调用过类B,而是调用了两次A.Process(…)方法。两者都包含传入的的的stageId(即,两个调用具有不同的stageId)

现在,如果我把事情分开一点,只是为了测试,我可以通过这样做来让事情正常工作:

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));
BlockingCollection bcs=new BlockingCollection();
A=零;
B=零;
foreach(管道中的阶段s。阶段)
{
IParallelPipe p=(IParallelPipe)Factory.GetPipe(s.type);
if(p是A)
a=p;
其他的
b=p;
}
Task.Factory.StartNew(()=>a.Process(ref-bcs,idThatINeed));
Task.Factory.StartNew(()=>b.Process(ref-bcs,idThatINeed));
这将调用相应的类


有什么想法吗

您描述的行为对我来说似乎很奇怪-我希望使用正确的实例,但可能使用错误的阶段ID-问题。正在捕获变量
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 bcs=new BlockingCollection();
foreach(管道中的阶段s。阶段)
{
阶段拷贝=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)); 
}
BlockingCollection bcs=new BlockingCollection();
foreach(管道中的阶段s。阶段)
{
长id=s.id;
IParallelPipe p=(IParallelPipe)Factory.GetPipe(s.type);
Task.Factory.StartNew(()=>p.Process(ref-bcs,id));
}

如果这没有帮助,你能发布一个简短但完整的程序来演示这个问题吗?这将使追踪更容易。

我认为同样的事情,“这与p无关”,这就是为什么我没有想到foreach问题。不过,我添加了“阶段副本”,问题就解决了。有趣。将进行更多的调查…是的,我在循环中声明p。