首页 > 代码库 > C# 线程基础

C# 线程基础

1. 线程的基本概念

简单的讲进程就是程序分配在内存当中,等待处理器进行处理,请记住线程会消耗大量的操作系统资源。多个线程共享一个物理处理器将导致处理器忙于处理管理这些进程,而无法运行程序。

使用线程通常是一个操作系统的任务,试图在一个单核CPU上并行执行计算任务是没有任何意义的,可能比顺序执行花费的时间更长。

为了更好的利用现代处理器的能力,使用多线程处理程序发挥出最好的作用,这需要组织多个线程间的通讯和相互同步。

下面将学习一下 线程的生命周期,和创建线程、挂起线程、线程等待、以及终止线程。

创建一个线程操作

        static void Main(string[] args)        {            //创建一个新的线程来实现输出数字            Thread t = new Thread(PrintNumber);            t.Start();            //这一行代码是在创建了一个新的线程并行执行的            PrintNumber();            Console.ReadLine();        }        static void PrintNumber()         {            Console.WriteLine("Starting...");            for (int i = 0; i < 10; i++)            {                Console.WriteLine(i);            }        }

从上面代码可以看出一个程序始终有一个主线程在执行,而Thread是创建一个新的线程执行。两者之间是同步执行的。

暂停线程

        static void Main(string[] args)        {            //创建一个带暂停的进程            Thread t = new Thread(PrintNumberWithdelay);            t.Start();            //这一行就是始终执行的主线程经过(一个程序都有一个始终执行的主线程)            PrintNumber();        }        static void PrintNumber()         {            Console.WriteLine("Starting...");            for (int i = 0; i < 10; i++)            {                Console.WriteLine(i);            }        }        static void PrintNumberWithdelay()         {            Console.WriteLine("暂停...");            for (int i = 0; i < 10; i++)            {                Thread.Sleep(TimeSpan.FromSeconds(2));                Console.WriteLine(i);            }        }

很明显,主线程已经执行完毕,而新的线程在输出暂停以后还在继续执行。每次执行都会休眠2秒钟。

线程等待

那么如何不让主线程继续往下执行,而是等待新线程执行完毕再往下执行呢。

        static void Main(string[] args)        {            //创建一个带暂停的进程            Thread t = new Thread(PrintNumberWithdelay);            t.Start();            t.Join();//线程等待            Console.WriteLine("执行完毕了");                 }        static void PrintNumberWithdelay()        {            Console.WriteLine("暂停...");            for (int i = 0; i < 10; i++)            {                Thread.Sleep(TimeSpan.FromSeconds(1));                Console.WriteLine(i);            }        }

终止线程

        static void Main(string[] args)        {            //创建一个带暂停的进程            Thread t = new Thread(PrintNumberWithdelay);            t.Start();            Thread.Sleep(TimeSpan.FromSeconds(3));            t.Abort();//终止线程            Console.WriteLine("线程终止了");                 }

这段代码是给线程注入了ThreadAbortException方法,导致线程被终结,这样操作是非常危险的,因为该异常可能会导致整个应用程序都崩溃。

可以使用 ResetAbort 方法来拒绝被终止。

检测线程状态

        static void Main(string[] args)        {            //创建一个带暂停的进程            Thread t = new Thread(PrintNumberWithdelay);            Thread t1 = new Thread(DoNothing);            t1.Start();            t.Start();            for (int i = 0; i < 30; i++)            {                Console.WriteLine(t.ThreadState.ToString());            }                        Thread.Sleep(TimeSpan.FromSeconds(3));            t.Abort();//终止线程            Console.WriteLine("线程终止了");            Console.WriteLine(t.ThreadState.ToString());            Console.WriteLine(t1.ThreadState.ToString());        }        static void PrintNumberWithdelay()        {            Console.WriteLine("开始啦...");            Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());            for (int i = 0; i < 10; i++)            {                Thread.Sleep(TimeSpan.FromSeconds(1));                Console.WriteLine(i);            }        }        static void DoNothing()        {            Thread.Sleep(TimeSpan.FromSeconds(2));        }

当主程序定义了两个不同的线程,一个将会被终止,而另一个则会成功运行。线程状态位于Thread 对象的ThreadState属性中。ThreadState属性是一个C#枚举对象。

刚开始线程状态为Unstarted ,然后启动线程,并估计在一周期为30的迭代的区间中,线程状会从Running变为WitSleepJoin。

 

线程优先级

    class ThreadSample    {        private bool _isStopped = false;        public void Stop()        {            _isStopped = true;        }        public void CountNumbers()        {            long counter = 0;            while (!_isStopped)            {                counter++;            }            Console.WriteLine("{0} 和 {1,11}" + " count={2,13}",                Thread.CurrentThread.Name,                Thread.CurrentThread.Priority,                counter.ToString("NO"));        }    }    class Program    {        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();        }        static void Main(string[] args)        {            Console.WriteLine("线程状态:{0}",Thread.CurrentThread.Priority);            Console.WriteLine("开始");            RunThreads();                       Thread.Sleep(TimeSpan.FromSeconds(3));            Console.WriteLine("模拟CPU单核计算");            //让操作系统运行在第一个CPU第一个核心上            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);            RunThreads();//运行时间很长            Console.WriteLine("线程终止了");        }    }

此程序只用于演示,通常中无需使用这种方式。

前台线程和后台线程

    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} 和 {1}",                    Thread.CurrentThread.Name, i);            }        }    }    class Program    {        static void Main(string[] args)        {            var samppleforegroud = new ThreadSample(10);            var sampplebackgroup = new ThreadSample(20);            var threadOne = new Thread(samppleforegroud.CountNumbers);            threadOne.Name = "前台";            var threadTwo = new Thread(sampplebackgroup.CountNumbers);            threadTwo.Name = "后台";            threadTwo.IsBackground = true;            threadOne.Start();            threadTwo.Start();        }    }

显示创建的是前台线程, ThreadTwo是后台线程 ,通过配置第一个线程会比第二个线程先完成,前台线程如果执行完毕,那么也会把后台线程终止掉。

 

向线程传递参数

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} 和 {1}",                    Thread.CurrentThread.Name, i);            }        }    }    class Program    {        static void Count(object i)        {            CountNumbers((int)i);        }        static void CountNumbers(int number)        {            Console.WriteLine(number);        }        static void PrintNumber(int number)        {            Console.WriteLine(number);        }        static void Main(string[] args)        {            var samppleforegroud = new ThreadSample(10);            var threadOne = new Thread(samppleforegroud.CountNumbers);            threadOne.Name = "One";            threadOne.Start();            threadOne.Join();            Console.WriteLine("------------");            var threadTwo = new Thread(Count);            threadTwo.Name = "Two";            threadTwo.Start(8);            threadTwo.Join();            var threaThree = new Thread(() => CountNumbers(12));            threaThree.Name = "Three";            threaThree.Start();            threaThree.Join();            int i = 10;            var threaFour = new Thread(() => PrintNumber(i));            i = 20;            var threaFour1 = new Thread(() => PrintNumber(i));            threaFour.Start();            threaFour1.Start();        }    }

使用Lock

abstract class CountBase    {        public abstract void Increment();        public abstract void Decrement();    }     class Counter : CountBase    {        public int Count { get; private set; }        public override void Increment()        {            Count++;        }        public override void Decrement()        {            Count--;        }    }     class CounterWithLock : CountBase    {        private readonly object _syncroot = new object();        public int Count { get; private set; }        public override void Increment()        {            lock (_syncroot)            {                Count++;            }        }        public override void Decrement()        {            lock (_syncroot)            {                Count--;            }        }    }    class Program    {        static void TestCouner(CountBase c)        {            for (int i = 0; i < 100000; i++)            {                c.Increment();                c.Decrement();            }        }        static void Main(string[] args)        {            var c = new Counter();            var t1 = new Thread(() => TestCouner(c));            var t2 = new Thread(() => TestCouner(c));            var t3 = new Thread(() => TestCouner(c));            t1.Start();            t2.Start();            t3.Start();            t1.Join();            t2.Join();            t3.Join();            Console.WriteLine(c.Count);            var c1 = new CounterWithLock();             t1 = new Thread(() => TestCouner(c));             t2 = new Thread(() => TestCouner(c));             t3 = new Thread(() => TestCouner(c));             t1.Start();             t2.Start();             t3.Start();             t1.Join();             t2.Join();             t3.Join();             Console.WriteLine(c.Count);        }    }

 

使用Monitor 锁定资源

为了避免死锁,则使用Monitor 类 来避免死锁。

Monitor.TryEnter(lock1,TimeSpan.FromSeconds(5));

C# 线程基础