C#多线程学习——线程基础(C#多线程编程实战)

一、 创建和使用线程

using System;
using System.Threading;

namespace Chapter1.Recipe1
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(PrintNumbers);            //创建一个新线程
            t.Start();
            PrintNumbers();
        }

        static void PrintNumbers()
        {
            Console.WriteLine("Starting...");
            for (int i = 1; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }
    }
}
Create Thread

在创建线程前,需要引用命名空间 using System.Threading;

创建好线程后,我们可以使用一些方法对线程操作:

方法操作
Start 使线程开始运行。
Sleep 使线程暂停指定的一段时间。
Suspend 线程到达某一安全点时暂停。
Abort 线程到达某一安全点时停止。
Resume 重新启动挂起的线程
Join 使当前线程等待其他线程完成。 如果与超时值配合使用,当线程在分配的时间内完成时,此方法会返回 True

二、线程属性

线程还包含多个有用的属性,如下表中所示:

属性
IsAlive 如果线程处于活动状态,将包含值 True
IsBackground 获取或设置布尔值,该值指示线程是否为或应为后台线程。 后台线程类似前台线程,但后台线程不会阻止进程停止。 属于某个进程的所有前台线程均停止后,公共语言运行时通过对仍处于活动状态的后台进程调用 Abort 方法来结束进程。
Name 获取或设置线程的名称。 最常用于在调试时查找各个线程。
Priority 获取或设置由操作系统用来确定线程计划优先顺序的值。
ThreadState 包含描述线程状态的值。

三、线程的优先级

using System;
using System.Diagnostics;
using System.Threading;

namespace Chapter1.Recipe6
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority);
            Console.WriteLine("Running on all cores available");
            RunThreads();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("Running on a single core");
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
            RunThreads();
        }

        static void RunThreads()
        {
            var sample = new ThreadSample();

            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            var threadTwo = new Thread(sample.CountNumbers);
            threadTwo.Name = "ThreadTwo";

            threadOne.Priority = ThreadPriority.Highest;                //这个修改了线程的优先级
            threadTwo.Priority = ThreadPriority.Lowest;
            threadOne.Start();
            threadTwo.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();
        }

        class ThreadSample
        {
            private bool _isStopped = false;

            public void Stop()
            {
                _isStopped = true;
            }

            public void CountNumbers()
            {
                long counter = 0;

                while (!_isStopped)
                {
                    counter++;
                }

                Console.WriteLine("{0} with {1,11} priority " +
                            "has a count = {2,13}", Thread.CurrentThread.Name,
                            Thread.CurrentThread.Priority,
                            counter.ToString("N0"));
            }
        }
    }
}
Thread Priority

线程有如下优先级:

  • Highest

  • AboveNormal

  • Normal

  • BelowNormal

  • Lowest

 

四、前后台程序

using System;
using System.Threading;

namespace Chapter1.Recipe7
{
    class Program
    {
        static void Main(string[] args)
        {
            var sampleForeground = new ThreadSample(10);
            var sampleBackground = new ThreadSample(20);

            var threadOne = new Thread(sampleForeground.CountNumbers);
            threadOne.Name = "ForegroundThread";
            var threadTwo = new Thread(sampleBackground.CountNumbers);
            threadTwo.Name = "BackgroundThread";
            threadTwo.IsBackground = true;                                  //这里修改的线程属性为后台运行

            threadOne.Start();
            threadTwo.Start();
        }

        class ThreadSample
        {
            private readonly int _iterations;

            public ThreadSample(int iterations)
            {
                _iterations = iterations;
            }
            public void CountNumbers()
            {
                for (int i = 0; i < _iterations; i++)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.5));
                    Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
                }
            }
        }
    }
}
Thread background

默认情况下,创建的是前台线程,手动设置IsBackground属性为true为后台线程。

前后台主要区别:进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作。

如果程序定义了一个不会完成的前台线程,那么主程序也不会正常结束。

上面例子里,程序运行到10就结束了。

 

五、向线程传递参数

using System;
using System.Threading;

namespace Chapter1.Recipe8
{
    class Program
    {
        static void Main(string[] args)
        {
            var sample = new ThreadSample(10);

            var threadOne = new Thread(sample.CountNumbers);            //使用sample的构造函数间接传递了值
            threadOne.Name = "ThreadOne";
            threadOne.Start();
            threadOne.Join();

            Console.WriteLine("--------------------------");

            var threadTwo = new Thread(Count);                          //用Count方法启动线程
            threadTwo.Name = "ThreadTwo";
            threadTwo.Start(8);                                         //Start传入了参数
            threadTwo.Join();

            Console.WriteLine("--------------------------");

            var threadThree = new Thread(() => CountNumbers(12));       //lamba表达式定义了不属于任何类的方法,创建一个方法,该方法使用需要的参数调用了另一个方法,并在另一个线程中运行该方法
            threadThree.Name = "ThreadThree";
            threadThree.Start();
            threadThree.Join();
            Console.WriteLine("--------------------------");

            int i = 10;
            var threadFour = new Thread(() => PrintNumber(i));          //lamba表达式中使用任何局部变量时,C#会生辰类,将该变量做为类的属性。
            i = 20;
            var threadFive = new Thread(() => PrintNumber(i));          //lamba表达式共享该变量值,所以都会打印20
            threadFour.Start(); 
            threadFive.Start();
        }

        static void Count(object iterations)                        //此处是为了接收thread.Start(8)创建的参数object类型
        {
            CountNumbers((int)iterations);                          //start传递的内容转换成了整形
        }

        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);
                }
            }
        }
    }

    
}
Thread parameter

构造函数间接传递,object---->start()传递,lamba间接传递。

 

六、lock关键字

using System;
using System.Threading;

namespace Chapter1.Recipe9
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Incorrect counter");

            var c = new Counter();

            var t1 = new Thread(() => TestCounter(c));
            var t2 = new Thread(() => TestCounter(c));
            var t3 = new Thread(() => TestCounter(c));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();

            Console.WriteLine("Total count: {0}",c.Count);
            Console.WriteLine("--------------------------");

            Console.WriteLine("Correct counter");

            var c1 = new CounterWithLock();

            t1 = new Thread(() => TestCounter(c1));
            t2 = new Thread(() => TestCounter(c1));
            t3 = new Thread(() => TestCounter(c1));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total count: {0}", c1.Count);

        }

        static void TestCounter(CounterBase c)
        {
            for (int i = 0; i < 100000; i++)                                //先调用增,后调用减
            {
                c.Increment();
                c.Decrement();
            }
        }

        class Counter : CounterBase                                         //不使用lock
        {
            public int Count { get; private set; }

            public override void Increment()
            {
                Count++;
            }

            public override void Decrement()
            {
                Count--;
            }
        }

        class CounterWithLock : CounterBase
        {
            private readonly object _syncRoot = new Object();

            public int Count { get; private set; }

            public override void Increment()
            {
                lock (_syncRoot)                                //使用lock
                {
                    Count++;
                }
            }

            public override void Decrement()
            {
                lock (_syncRoot)                                //使用lock
                {
                    Count--;
                }
            }
        }

        abstract class CounterBase                              //创建了抽象类?
        {
            public abstract void Increment();

            public abstract void Decrement();
        }
    }
}
Thread lock

三个线程共用counter实例,所以多个线程调用时。如果没有lock,两个线程调用后,可能会同时减去了1,但不同时加上1.或者同时加上了1,不同时减去1。

这样最后的结果多是情况下可能不是0。

 

七、死锁情况

using System;
using System.Threading;

namespace Chapter1.Recipe10
{
    class Program
    {
        static void Main(string[] args)
        {
            object lock1 = new object();
            object lock2 = new object();

            new Thread(() => LockTooMuch(lock1, lock2)).Start();                                //这里创建了线程1,

            lock (lock2)                                                                        //主线程中锁住lock2
            {
                Thread.Sleep(1000);
                Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
                if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))                           //主线程等待5秒,获取lock1,当然会失败
                {
                    Console.WriteLine("Acquired a protected resource succesfully");
                }
                else
                {
                    Console.WriteLine("Timeout acquiring a resource!");
                }
            }

            new Thread(() => LockTooMuch(lock1, lock2)).Start();

            Console.WriteLine("----------------------------------");
            lock (lock2)                                                                        //这时创建的线程2,lock1在线程1中死锁。所以无法获取lock1,两个线程死锁了
            {
                Console.WriteLine("This will be a deadlock!");
                Thread.Sleep(1000);
                lock (lock1)
                {
                    Console.WriteLine("Acquired a protected resource succesfully");
                }
            }
        }

        static void LockTooMuch(object lock1, object lock2)                                     //先锁住lock1,然后锁住lock2
        {
            lock (lock1)
            {
                Thread.Sleep(1000);                                                             //等待1秒
                lock (lock2);
            }
        }
    }
}
deadlock!

线程1和主线程发生了简单的死锁,但是主线程是使用TryEnter。所以处理了死锁的情况,释放了lock2。但是线程1没有错误处理,所以没有释放lock1。

然后到线程2时,同样使用了lock2。但是lock1无法获取了,因为线程1没有释放?

 

八、错误处理

using System;
using System.Threading;

namespace Chapter1.Recipe11
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = new Thread(FaultyThread);
            t.Start();
            t.Join();

            try
            {
                t = new Thread(BadFaultyThread);
                t.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("We won't get here!");
            }
        }

        static void BadFaultyThread()
        {
            Console.WriteLine("Starting a faulty thread...");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            throw new Exception("Boom!");
        }

        static void FaultyThread()
        {
            try
            {
                Console.WriteLine("Starting a faulty thread...");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                throw new Exception("Boom!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception handled: {0}", ex.Message);
            }
        }
    }
}
try catch

如果直接使用线程,一般来说不要在线程中抛出异常,而是在线程代码中使用try/catch代码块

 

posted @ 2017-08-20 15:11  习惯就好233  阅读(167)  评论(0编辑  收藏  举报