linq lambdas如何在循环中工作?

时间:2010-09-26 10:30:48

标签: c# multithreading linq lambda

我正在尝试在我的应用程序中实现任务。

以下是示例代码:

有一个简单的接口I,从它派生出3个类(A,B,C) 我创建一个Is列表,用A,B,C实例弹出它,然后为彼此创建一个任务来调用方法do1();

  interface I
    {
        void do1();

    }

    class A : I
    {
        public void do1()
        {
            Console.WriteLine("A");
        }
    }


    class B : I
    {
        public void do1()
        {
            Console.WriteLine("B");
        }
    }


    class C : I
    {
        public void do1()
        {
            Console.WriteLine("C");
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            List<I> l = new List<I>();
            l.Add(new A());
            l.Add(new B());
            l.Add(new C());


            var TaskPool = new List<Task>();


            foreach (var i in l)
            {
                Task task = new Task( () => i.do1()

                    );
                TaskPool.Add(task);
            }


            foreach (var c in TaskPool)
            {
                c.Start();
            }

            Thread.Sleep(3000);
            Console.Read();
        }


    }

我期待看到

A
B
C

在输出中,而不是我得到

C
C
C

我在调试器中找到了问题:所有任务都有相同的委托,但我不知道为什么以及如何解决这个问题。

3 个答案:

答案 0 :(得分:7)

这是一个非常常见的问题。它涉及“捕获的变量”如何工作;简短版本,你需要这个:

foreach (var i in l)
{
    var copy = i;
    Task task = new Task( () => copy.do1());
    TaskPool.Add(task);
}

这里的问题是i(来自foreach技术上声明之外的循环范围,因此被捕获在外部范围内;每次捕获相同的变量(C#捕获变量,而不是*值)。在中添加copy 环路范围会改变这一点;由于范围,copy每次迭代单独捕获

答案 1 :(得分:5)

问题是在分配代理时捕获循环变量i。如果在分配之前创建临时副本,则可以避免捕获变量,从而获得所需的值。

foreach (var i in l)
{
    var local = i;
    Task task = new Task( () => local.do1());
    TaskPool.Add(task);
}

答案 2 :(得分:3)

这是linq表达式的预期行为。它有点被称为变量捕获。有关此主题的一些详细信息,请参阅this link

在您的情况下,只需用方法组替换linq表达式。我的意思是: 此...

Task task = new Task(i.do1);

而不是......

Task task = new Task(() => i.do1());

编辑:还有一件非常重要的事情。您已通过添加A,B,C(按特定顺序)将项目添加到列表中。这并不能保证A任务在B任务之前运行,依此类推。你可以得到任何东西作为输出,ABC,ACB等。