首页 > 解决方案 > C# 使基本多线程循环返回正确的值

问题描述

首先,我是 C# 新手,这是我编写的第一个使用线程的程序。对不起,如果它有点基本。

这是我的应用程序:

    class Program
{
    static void AddNumbers()
    {
        int count = 0; 

        for (int counter = 0; counter < 90000000; counter++)
        {
            count += counter;
        }
    }

    static void Main()
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        for (int i = 0; i < 50; i++)
        {
            // Non-threaded
            //AddNumbers();
            //Console.WriteLine((i + 1).ToString());

            // Threaded
            new Thread(() =>
            {
                //Thread.CurrentThread.IsBackground = true;

                AddNumbers();
                Thread.Sleep(1000);

                Console.WriteLine((i + 1).ToString());
            }).Start();
        }

        stopwatch.Stop();
        Console.WriteLine("Time elapsed: {0:hh\\:mm\\:ss}", stopwatch.Elapsed);

        Console.ReadLine();
    }
}

它只是循环i时间并每次添加一堆数字。当我运行AddNumbers()非多线程版本时,它会正确显示1, 2, 3...等,但是当我多线程运行它时,它会返回 2-3 次相同的索引,跳过一些,并在所有线程执行之前返回秒表值。

有人可以帮助找出我的错误在哪里,最重要的是澄清我对线程如何在 C# 中工作的想法?谢谢!

标签: c#multithreading

解决方案


当我运行非多线程的 AddNumbers() 版本时,它正确显示 1、2、3... 等,但是当我多线程运行它时,它返回相同的索引 2-3 次,跳过一些

它发生了,因为i被捕获,但在外部循环中发生了变化。

在此处阅读更多信息:

在 C# http://www.trycatchthat.com/csharp/fundamentals/2016/02/29/csharp-closure-loops.html的循环中捕获的变量

为了使其正常工作,您需要保存本地副本:

for (int i = 0; i < 50; i++)
{
    // Non-threaded
    //AddNumbers();
    //Console.WriteLine((i + 1).ToString());

    // Threaded
    int iCopy = i;
    new Thread(() =>
    {
        //Thread.CurrentThread.IsBackground = true;

        AddNumbers();
        Thread.Sleep(1000);

        Console.WriteLine((iCopy + 1).ToString());
    }).Start();
}

在所有线程执行之前返回秒表值

它发生了,因为你实际上从来没有加入你的线程并等待它们执行。
它可以通过存储所有线程并调用来实现thread.Join()

Thread[] threads = new Thread[50];

for (int i = 0; i < 50; i++)
{
    // Non-threaded
    //AddNumbers();
    //Console.WriteLine((i + 1).ToString());

    // Threaded
    int iCopy = i;
    threads[i] = new Thread(() =>
    {
        //Thread.CurrentThread.IsBackground = true;

        AddNumbers();
        Thread.Sleep(1000);

        Console.WriteLine((iCopy + 1).ToString());
    });
    thread[i].Start();
}

for (int i = 0; i < 50; i++)
    threads[i].Join();

在此处阅读有关等待线程完成的更多信息:

创建多个线程并等待所有线程完成

在 TPL 发明之前,人们就是这样做的。
现在,一般规则不是使用Thread,而是使用TaskorParallel代替。

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

Task[] tasks = new Task[50];
for (int i = 0; i < 50; i++)
{
    int iCopy = i;
    tasks[i] = Task.Run(() => {
        AddNumbers();
        Thread.Sleep(1000);
        Console.WriteLine((iCopy + 1).ToString());
    });
} 

Task.WaitAll(tasks);

推荐阅读