试图理解C#中的多线程

xav*_*ier 4 c# multithreading new-operator

我正在尝试理解多线程的基础知识,所以我构建了一个小程序,提出了一些问题,我会感谢任何帮助:)

这是一个小程序:

class Program
{
    public static int count;
    public static int max;
    static void Main(string[] args)
    {
        int t = 0;
        DateTime Result;
        Console.WriteLine("Enter Max Number : ");
        max = int.Parse(Console.ReadLine());
        Console.WriteLine("Enter Thread Number : ");
        t = int.Parse(Console.ReadLine());

        count = 0;

        Result = DateTime.Now;
        List<Thread> MyThreads = new List<Thread>();
        for (int i = 1; i < 31; i++)
        {
            Thread Temp = new Thread(print);
            Temp.Name = i.ToString();
            MyThreads.Add(Temp);
        }

        foreach (Thread th in MyThreads)
            th.Start();

        while (count < max)
        {
        }

        Console.WriteLine("Finish , Took : " + (DateTime.Now - Result).ToString() + " With : " + t + " Threads.");
        Console.ReadLine();
    }

    public static void print()
    {
        while (count < max)
        {
            Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
            count++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我通过一些测试运行检查了这个:

我将最大数量设为100,似乎最快的执行时间是2个线程,比10个线程的时间快80%.

问题:

1)线程4-10甚至不打印一次,怎么会这样?

2)不应该更多的线程更快?

我将最大数量设为10000并禁用打印.

使用此配置,5个线程似乎是最快的.

为什么与第一次检查相比有变化?

而且在这种配置中(带打印)所有线程都会打印几次.为什么与第一次打印不同,只打印了几个线程?

有没有办法让所有线程逐个打印?在一条线或类似的东西?

非常感谢您的帮助 :)

use*_*116 7

您的代码肯定是进入线程世界的第一步,您刚刚经历了第一次(很多)头痛!

首先,static可以使您能够在线程之间共享变量,但它不以线程安全的方式执行.这意味着您的count < max表达count++并不保证是最新的或线程之间的有效保护.当max我的8处理器工作站上只有10(t设置为4)时,查看程序的输出:

T0 - 0
T0 - 1
T0 - 2
T0 - 3
T1 - 0 // wait T1 got count = 0 too!
T2 - 1 // and T2 got count = 1 too!
T2 - 6
T2 - 7
T2 - 8
T2 - 9
T0 - 4
T3 - 1 // and T3 got count = 1 too!
T1 - 5
Run Code Online (Sandbox Code Playgroud)

关于每个线程逐个打印的问题,我假设您正在尝试协调访问count.您可以使用同步原语(例如lockC#中的语句)来完成此操作.以下是对代码的简单修改,它将确保仅max发生增量:

static object countLock = new object();

public static void printWithLock()
{
    // loop forever
    while(true)
    {
        // protect access to count using a static object
        // now only 1 thread can use 'count' at a time
        lock (countLock)
        {
            if (count >= max) return;

            Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
            count++;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个简单的修改使您的程序在逻辑上正确,但也很慢.该示例现在出现了一个新问题:锁争用.每个线程现在都在争夺访问权限countLock.我们已经使程序线程安全,但没有并行性的任何好处!

线程和并行性并不是特别容易实现,但幸运的是.Net的最新版本带有任务并行库(TPL)并行LINQ(PLINQ).

图书馆的美妙之处在于转换当前代码是多么容易:

var sw = new Stopwatch();

sw.Start();
Enumerable.Range(0, max)
          .AsParallel()
          .ForAll(number =>
               Console.WriteLine("T{0}: {1}",
                                 Thread.CurrentThread.ManagedThreadId,
                                 number));

Console.WriteLine("{0} ms elapsed", sw.ElapsedMilliseconds);

// Sample output from max = 10
// 
// T9: 3
// T9: 4
// T9: 5
// T9: 6
// T9: 7
// T9: 8
// T9: 9
// T8: 1
// T7: 2
// T1: 0
// 30 ms elapsed
Run Code Online (Sandbox Code Playgroud)

上面的输出是一个有趣的例子,说明为什么线程会为新用户产生"意外结果".当线程并行执行时,它们可以在不同的时间点完成代码块,或者一个线程可能比另一个线程更快.你永远不会真正了解线程!