C# 多线程学习系列三:线程优先级、线程同步以及向线程传递参数

线程优先级

        在使用线程的时候,我们经常会见到线程优先级的问题,那么什么是线程优先级呢,线程优先级决定了该线程可占用多少的CPU时间。在C#程序中,可以对线程设定五个不同的优先级,从高到低依次是Highest、AboveNormal、Normal、BelowNormal和Lowest,在创建线程时,如果不指定其优先级,则系统默认为Normal。假如想让一些重要的线程优先执行,可以使用下面的方法为其赋予较高的优先级。

<span style="font-size:18px;">Thread t = new Thread(new ThreadStart(thread_method));
t.priority = ThreadPriority.AboveNormal;</span>

注意,当把某线程的优先级设为Highest时,系统上正在运行的其他线程都会终止,所以使用这个优先级是要特别小心。

线程同步

        使用线程的时候会碰到一个非常经典的问题,那就是线程同步所谓同步,是指多个线程之间存在先后执行顺序的关联关系。如果一个线程必须在另一个线程完成某个工作后才能执行,则必须考虑如何让其保持同步,以确保在系统运行多个线程而不会出现逻辑错误。比如下面的例子是多线程问题中比较经典的售票问题:

<span style="font-size:18px;">namespace TestThread
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");
            Thread thread1 = new Thread(run);
            Thread thread2 = new Thread(run);
            Thread thread3 = new Thread(run);
            thread1.Start();
            thread2.Start();
            thread3.Start();
            Console.ReadKey();
        }
        private static int count = 10;//火车票的数量  
        public static void run()
        {
            for (int i = 0; i < 100; i++)
            {
                if (count > 0)
                {//表示还有票 
                    try
                    {
                        Thread.Sleep(300);
                    }
                    catch (Exception e)
                    {
                        // TODO Auto-generated catch block  
                        Console.WriteLine(e.StackTrace);
                    }
                    Console.WriteLine("现在还剩{0},已经卖出{1}",--count,10-count);
                }
            }
        }
    }
}</span>
输出为:

C# 多线程学习系列三:线程优先级、线程同步以及向线程传递参数_第1张图片

        我们从输出中可以看到,三个线程分别执行了三次购买,从10变成了7,当300ms过后,再输出,我们发现了逻辑错误,这就是因为线程没有同步,导致数据共用,出现了逻辑错误。这也叫做竞争条件。那么我们怎么解决这个错误,其实很简单,在共用的数据上加上lock关键字,其逻辑是当已有线程操作count对象时,所有其他线程必须等待直到当前线程完成操作。如下所示:

<span style="font-size:18px;">namespace TestThread
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");
            Ticket ticket = new Ticket();
            Thread thread1 = new Thread(ticket.run);
            Thread thread2 = new Thread(ticket.run);
            Thread thread3 = new Thread(ticket.run);
            //Thread thread = new Thread(PrintEvenNumber);
            //thread.Start();
            //thread.Join();
            //PrintOddNumber();
            thread1.Start();
            thread2.Start();
            thread3.Start();
            Console.ReadKey();
        }

        class Ticket
        {
            private int count = 10;//火车票的数量
            public void run()
            {
                int num = count;
                for (int i = 0; i < 100; i++)
                {
                    lock (this)
                    {
                        if (count > 0)
                        {//表示还有票 
                            try
                            {
                                Thread.Sleep(300);
                                    --count;
                                    num = 10 - count;
                            }
                            catch (Exception e)
                            {
                                // TODO Auto-generated catch block  
                                Console.WriteLine(e.StackTrace);
                            }
                            Console.WriteLine("现在还剩{0},已经卖出{1}", count, num);
                        }
                    }
                    
                }
            }
        }
      }  
    }</span>
此时,输出为:

输出正确。

向线程传递参数

接下来来介绍向线程传递参数的几种方法,直接上代码:

<span style="font-size:18px;">namespace TestThread2
{
    class Program
    {
        static void Main(string[] args)
        {
            var sample = new ThreadSample(10);

            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            threadOne.Start();
            threadOne.Join();
            Console.WriteLine("------------------------------");

            var threadTwo = new Thread(Count);
            threadTwo.Name = "ThreadTwo";
            threadTwo.Start(8);
            threadTwo.Join();
            Console.WriteLine("-------------------------------");

            var threadThree = new Thread(() => CountNumbers(12));
            threadThree.Name = "ThreadThree";
            threadThree.Start();
            threadThree.Join();

            int i = 10;//特别注意
            var threadFour = new Thread(() => PrintNumber(i));
            i = 4;
            var threadFive = new Thread(() => PrintNumber(i));
            threadFour.Start();
            threadFive.Start();

            Console.ReadKey();
        }

        static void Count(object iterations)
        {
            CountNumbers((int)iterations);
        }
        static void CountNumbers(int iterations)
        {
            for (int i = 1; i<=iterations;i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }
        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }
        class ThreadSample
        {
            private readonly int _iterations;
            public ThreadSample(int iterations)
            {
                _iterations = iterations;
            }
            public void CountNumbers()
            {
                for (int i =1; i<=_iterations;i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
                }
            }
        }
    }
}</span>
输出为:


        线程使用另一个类中的方法以及静态方法比较简单,不再讲解,可以查看代码和输出进行理解,但是需要注意,当方法需要参数时,必须在start()方法中传递参数。

        线程三和线程四都使用了lambda表达式,lambda表达式定义了一个不属于任何类的方法。我们创建了一个方法,该方法使用需要的参数调用了另一个方法,并在另一个方法中运行该方法。当启动threadThree线程时,打印出了12个数字,这就是我们通过lambda表达式传递的数字。

        使用lambda表达式引用另一个C#对象的方法被称为闭包,当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性。所以,线程四和线程五实际上与线程一一样,但是我们没有定义该类,因为C#编译器会自动帮我们实现。

        这会导致几个问题,例如,如果在多个lambda表达式使用相同的变量,它们会共享该变量值。在上个例子中,当启动threadFour和threadFive线程时,它们都会打印4,因为在这两个线程启动之前变量被修改为4.



你可能感兴趣的:(多线程,线程,C#)