C#threading:循环计数器仅打印最后一个值

时间:2015-02-20 12:22:45

标签: c# multithreading

为什么这段代码:

private static void UdpPortListener(UInt16 Port)
{
    Console.WriteLine("Listening to port: {0}", Port);
}

static void Main(string[] args)
{
    List<Thread> ts = new List<Thread>();

    for(int i = 0; i < 20; i++)
    {
        Thread t = new Thread(() =>
        {
            UdpPortListener(Convert.ToUInt16(52000 + i));
        });

        t.IsBackground = false;

        ts.Add(t);
    }

    ts.ForEach((x) => x.Start());
}

产生这个输出:

Listening to port: 52020
Listening to port: 52020
...
Listening to port: 52020

当我编写这段代码时,我希望它能打印从52000开始递增的数字

3 个答案:

答案 0 :(得分:3)

它已经关闭了你的for循环变量。

在编译时提升i变量..因为它是一个循环计数器,它实际上是在循环之外访问的(在这里的线程委托中):

Thread t = new Thread(() =>
{
    UdpPortListener(Convert.ToUInt16(52000 + i));
}); //                                      ^^^ the compiler closes over this

这意味着,当您的Threads产生时,i方法会检查UdpPortListener的值... i的值for循环中的最后一个值..因为循环在它之前执行。

要修复此问题,您需要复制循环内的值:

var temp = i;
Thread t = new Thread(() =>
{     
    UdpPortListener(Convert.ToUInt16(52000 + temp));
});

答案 1 :(得分:2)

这是由于封闭效应造成的。试试这个:

static void Main(string[] args)
{
    List<Thread> ts = new List<Thread>();

    for(int i = 0; i < 20; i++)
    {
        var closureIndex = i;
        Thread t = new Thread(() =>
        {
            UdpPortListener(Convert.ToUInt16(52000 + closureIndex));
        });

        t.IsBackground = false;

        ts.Add(t);
    }

    ts.ForEach((x) => x.Start());
}

答案 2 :(得分:1)

代码有99个问题(只是开玩笑),封闭就是其中之一:)。此外,您不需要完整的线程,您可以使用任务。

void static Main()
{
    List<  System.Threading.Tasks.Task> ts = new List<  System.Threading.Tasks.Task>();

    for(int i = 0; i < 20; i++) // why not have i = 52000 ???
    {
        var  num = Convert.ToUInt16(52000 + i);
        var t = new   System.Threading.Tasks.Task(() =>
        {
            UdpPortListener(num);
        });


        ts.Add(t);
    }

    ts.ForEach((x) => x.Start());
}

// Define other methods and classes here

private static void UdpPortListener(UInt16 Port)
{
    Console.WriteLine("Listening to port: {0}", Port);
}