首页 > 编程语言 > 详细

进程和线程(线程是轻量级进程)(中)

时间:2020-01-14 16:06:32      阅读:83      评论:0      收藏:0      [点我收藏+]

创建线程

线程是通过扩展 Thread 类创建的。扩展的 Thread 类调用 Start() 方法来开始子线程的执行。

using System;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(new ThreadStart(PrintNumbers));//无参数的委托
            t1.Start();
            
            Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有参数的委托
            t2.Start(10);
            Console.ReadLine();
        }

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

        //注意:要使用ParameterizedThreadStart,定义的参数必须为object
        //如果使用的是不带参数的委托,不能使用带参数的Start方法运行线程,否则系统会抛出异常。但使用带参数的委托,可以使用thread.Start()来运行线程,这时所传递的参数值为null。
        static void PrintNumbers(object count)
        {
            Console.WriteLine("Starting...");
            for (int i = 0; i < Convert.ToInt32(count); i++)
            {
                Console.WriteLine(i);
            }
        }
    }
}

 

注释:我们只需指定在不同线程运行的方法名,而C#编译器会在后台创建这些对象

 

技术分享图片
技术分享图片

如果为了简单,也可以通过匿名委托或Lambda表达式来为Thread的构造方法赋值

static void Main(string[] args)
 {
       //通过匿名委托创建
       Thread thread1 = new Thread(delegate() { Console.WriteLine("我是通过匿名委托创建的线程"); });
       thread1.Start();
       //通过Lambda表达式创建
       Thread thread2 = new Thread(() => Console.WriteLine("我是通过Lambda表达式创建的委托"));
       thread2.Start();
       Console.ReadKey();
 }

 

运行结果:

技术分享图片

管理线程

Thread 类提供了各种管理线程的方法。

下面的实例演示了 sleep() 方法的使用,用于在一个特定的时间暂停线程。

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            // 线程暂停 5000 毫秒
            int sleepfor = 5000; 
            Console.WriteLine("Child Thread Paused for {0} seconds", 
                              sleepfor / 1000);
            Thread.Sleep(sleepfor);
            Console.WriteLine("Child thread resumes");
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

 

注释:也可使用Thread.Sleep(TimeSpan.FromSeconds(5));暂停线程

销毁线程

Abort() 方法用于销毁线程。

通过抛出 threadabortexception 在运行时中止线程。这个异常不能被捕获,如果有 finally 块,控制会被送至 finally 块。

下面的程序说明了这点:

技术分享图片
using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {
                Console.WriteLine("Child thread starts");
                // 计数到 10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Child Thread Completed");
            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Thread Abort Exception");
            }
            finally
            {
                Console.WriteLine("Couldn‘t catch the Thread Exception");
            }
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // 停止主线程一段时间
            Thread.Sleep(2000);
            // 现在中止子线程
            Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}
View Code

 

当上面的代码被编译和执行时,它会产生下列结果:

1 In Main: Creating the Child thread
2 Child thread starts
3 0
4 1
5 2
6 In Main: Aborting the Child thread
7 Thread Abort Exception
8 Couldn‘t catch the Thread Exception

线程等待

using System;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting...");
            Thread t = new Thread(PrintNumbersWithDelay);
            t.Start();
            t.Join();   //使用Join等待t完成
            PrintNumbers();
            Console.WriteLine("THread Complete");
            Console.ReadLine();
        }

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

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("Starting...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }
    }
}

 

注释:使用t.Join();   等待t完成

检测线程状态

技术分享图片
using System;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Start Program...");
            Thread t1 = new Thread(PrintNumbersWithStatus);
            Thread t2 = new Thread(DoNothing);
            Console.WriteLine(t1.ThreadState.ToString());//获取实例线程状态
            t2.Start();
            t1.Start();
            for (int i = 0; i < 30; i++)
            {
                Console.WriteLine(t1.ThreadState.ToString());
            }
            Thread.Sleep(TimeSpan.FromSeconds(6));
            t1.Abort();
            Console.WriteLine("thread t1 has been aborted");
            Console.WriteLine(t1.ThreadState.ToString());
            Console.WriteLine(t2.ThreadState.ToString());
            Console.ReadLine();
        }

        private static void PrintNumbersWithStatus()
        {
            Console.WriteLine("Starting...");
            Console.WriteLine(Thread.CurrentThread.ThreadState.ToString());//获取当前线程状态
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }

        private static void DoNothing()
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }
    }
}
View Code

 

注释:使用Thread.ThreadState获取线程的运行状态。ThreadState是一个C#枚举。谨记:不要在程序中使用线程终止,否则可能会出现意想不到的结果

前台线程和后台线程

前台线程:只有所有的前台线程都结束,应用程序才能结束。默认情况下创建的线程都是前台线程

后台线程:只要所有的前台线程结束,后台线程自动结束。通过Thread.IsBackground设置后台线程。必须在调用Start方法之前设置线程的类型,否则一旦线程运行,将无法改变其类型。

通过BeginXXX方法运行的线程都是后台线程。

技术分享图片
using System;
using System.Diagnostics;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {                   
            //演示前台、后台线程
            BackGroundTest background = new BackGroundTest(10);
            //创建前台线程
            Thread fThread = new Thread(new ThreadStart(background.RunLoop));
            //给线程命名
            fThread.Name = "前台线程";
            
            BackGroundTest background1 = new BackGroundTest(20);
            //创建后台线程
            Thread bThread = new Thread(new ThreadStart(background1.RunLoop));
            bThread.Name = "后台线程";
            //设置为后台线程
            bThread.IsBackground = true;

            //启动线程
            fThread.Start();
            bThread.Start();
        }
    }

    class BackGroundTest
    {
        private int Count;
        public BackGroundTest(int count)
        {
            this.Count = count;
        }
        public void RunLoop()
        {
            //获取当前线程的名称
            string threadName = Thread.CurrentThread.Name;
            for (int i = 0; i < Count; i++)
            {
                Console.WriteLine("{0}计数:{1}",threadName,i.ToString());
                //线程休眠500毫秒
                Thread.Sleep(1000);
            }
            Console.WriteLine("{0}完成计数",threadName);
            
        }
    }
}
View Code

 

运行结果:前台线程执行完,后台线程未执行完,程序自动结束。

技术分享图片

把bThread.IsBackground = true注释掉,运行结果:主线程执行完毕后(Main函数),程序并未结束,而是要等所有的前台线程结束以后才会结束。

技术分享图片

后台线程一般用于处理不重要的事情,应用程序结束时,后台线程是否执行完成对整个应用程序没有影响。如果要执行的事情很重要,需要将线程设置为前台线程。

向线程传递参数

技术分享图片
using System;
using System.Diagnostics;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadSample sample = new ThreadSample(5);

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

            Thread t2 = new Thread(Count);
            t2.Name = "ThreadTwo";
            t2.Start(3);
            t2.Join();
            Console.WriteLine("--------------------------");

            //使用lambda表达式引用另一个C#对方的方式被称为闭包。当在lambda表达式中使用任何局部变量时,C#会生成一个类,并将该变量作为该类的一个属性,但是我们无须定义该类,C#编译器会自动帮我们实现
            Thread t3 = new Thread(()=> CountNumbers(5));
            t3.Name = "ThreadThree";
            t3.Start();
            t3.Join();
            Console.WriteLine("--------------------------");

            int i = 10;
            Thread t4 = new Thread(() => PrintNumber(i));
            
            i = 20;
            Thread t5 = new Thread(() => PrintNumber(i));
            t4.Start();
            t5.Start();
            //t4, t5都会输出20, 因为t4,t5没有Start之前i已经变成20了
            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($"{Thread.CurrentThread.Name} prints {i}");
            }
        }

        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }
    }

    class ThreadSample
    {
        private readonly int _iteration;

        public ThreadSample(int iteration)
        {
            _iteration = iteration;
        }

        public void CountNumbers()
        {
            for (int i = 1; i <= _iteration; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
            }
        }
    }
}
View Code

 

注释也可以使用ThreadStart传递参数

使用C# lock关键字

技术分享图片
sing System;
using System.Diagnostics;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Incorrect Counter");
            Counter c1 = new Counter();
            var t1 = new Thread(() => TestCounter(c1));
            var t2 = new Thread(() => TestCounter(c1));
            var t3 = new Thread(() => TestCounter(c1));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine($"Total Count: {c1.Count}");
            Console.WriteLine("------------------------");

            Console.WriteLine("Correct counter");
            CounterWithLock c2 = new CounterWithLock();
            t1 = new Thread(() => TestCounter(c2));
            t2 = new Thread(() => TestCounter(c2));
            t3 = new Thread(() => TestCounter(c2));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine($"Total count:{c2.Count}");
            Console.ReadLine();
        }

        static void TestCounter(CounterBase c)
        {
            for (int i = 0; i < 100000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }

        class Counter : CounterBase
        {
            public int Count { get; private set; }
            public override void Decrement()
            {
                Count--;
            }

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

        class CounterWithLock : CounterBase
        {
            private readonly object _asyncRoot = new object();
            public int Count { get; private set; }
            public override void Decrement()
            {
                lock (_asyncRoot)
                {
                    Count--;
                }
            }

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

        abstract class CounterBase
        {
            public abstract void Increment();

            public abstract void Decrement();
        }
    }

    class ThreadSample
    {
        private readonly int _iteration;

        public ThreadSample(int iteration)
        {
            _iteration = iteration;
        }

        public void CountNumbers()
        {
            for (int i = 1; i <= _iteration; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}");
            }
        }
    }
}
View Code

 

注释不加锁,得出的结果不确定,竞争条件下很容易出错。加锁得出的结果是正确的,但是性能受到了影响

使用Monitor类锁定资源

技术分享图片
using System;
using System.Diagnostics;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            object lock1 = new object();
            object lock2 = new object();
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            lock (lock2)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
                //直接使用Monitor.TryEnter, 如果在第二个参数之前还未获取到lock保护的资源会返回false
                if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
                {
                    Console.WriteLine("Acquired a protected resource successfully");
                }
                else
                {
                    Console.WriteLine("Timeout acquiring a resource");
                }
            }
            new Thread(() => LockTooMuch(lock1, lock2)).Start();
            Console.WriteLine("-----------------------------");
            /* 下面代码会造成死锁, 所以注释掉
            lock (lock2)
            {
                Console.WriteLine("This will be a deadlock!");
                Thread.Sleep(1000);
                lock (lock1)
                {
                    Console.WriteLine("Acquired a protected resource successfully");
                }
            }
            */
        }

        static void LockTooMuch(object lock1, object lock2)
        {
            lock (lock1)
            {
                Thread.Sleep(1000);
                lock (lock2);
            }
        }
    }
}
View Code

 

 

注释:Monitor.TryEnter在指定的时间内尝试获取指定对象上的排他锁

处理异常

技术分享图片
using System;
using System.Diagnostics;
using System.Threading;

namespace MulityThreadNote
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread 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));
            //这个异常主线程无法捕捉到,因为是在子线程抛出的异常。需要在子线程中加入try...catch捕获异常
            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: {ex.Message}");
            }
        }
    }
}
View Code

 

 

 

 

 

 

 

 

进程和线程(线程是轻量级进程)(中)

原文:https://www.cnblogs.com/zhaoyl9/p/12191973.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!