欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#线程学习笔记一:线程基础

程序员文章站 2022-04-14 18:57:31
本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/Thread.html,记录一下学习过程以备后续查用。 一、线程的介绍 进程(Process)是应用程序的实例要使用的资源的一个集合,每个应用程序都在各自的进程中运行来确保应用程序不受其他 ......

    本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/thread.html,记录一下学习过程以备后续查用。

    一、线程的介绍

    进程(process)是应用程序的实例要使用的资源的一个集合,每个应用程序都在各自的进程中运行来确保应用程序不受其他应用程序的影响。

    线程是进程中基本执行单元, 一个进程中可以包含多个线程。在进程入口执行的第一个线程是一个进程的主线程,在.net应用程序中,都是以main()方法

作为程序的入口(线程是进程的执行单元,进程是线程的一个容器)。

    二、线程调度和优先级  

    windows之所以被称为抢占式多线程操作系统,是因为线程可以在任意时间被抢占,并调度另一个线程。

    每个线程都分配了从0~31的一个优先级,系统首先把高优先级的线程分配给cpu执行。

    windows 支持7个相对线程优先级:idle、lowest、below normal、normal、above normal、highest和time-critical。normal是默认的线程优先级,

然而在程序中可以通过设置thread的priority属性来改变线程的优先级,它的类型为threadpriority枚举类型:lowest、belownormal、normal、abovenormal

和highest,clr为自己保留了 idle和time-critical优先级。

    枚举值列表如下:

成员名称 说明
lowest 可以将thread置于其他优先级线程之后。
belownormal 可以将thread置于normal优先级线程之后lowest优先级线程之前。
normal

可以将thread置于abovenormal优先级线程之后belownormal优先级线程之前。

默认情况下,线程置于normal优先级。

abovenormal 可以将thread置于highest优先级线程之后normal优先级线程之前。
highest 可以将thread置于其他优先级线程之前。

    三、前台线程和后台线程

    在.net中线程分为前台线程和后台线程:

    i、主线程是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程,它是前台线程。 

    ii、子线程可以是前台线程也可以是后台线程。

    iii、前台线程必须全部执行完,即使主线程关闭掉,这时进程仍然存活。

    iv、当所有前台线程停止运行时,clr会强制结束仍在运行的任何后台线程,这些后台线程直接被终止,不会抛出异常。

    v、前台线程与后台线程唯一的区别是后台线程不会阻止进程终止,可以在任何时候将前台线程修改为后台线程。

        static void main(string[] args)
        {
            threadtype();
        }

        /// <summary>
        /// 前台线程与后台线程
        /// </summary>
        private static void threadtype()
        {
            //创建一个新线程(默认为前台线程)
            thread backthread = new thread(worker)
            {
                //将线程更改为后台线程
                isbackground = true
            };

            //通过start方法启动线程
            backthread.start();

            //如果backthread是前台线程,则应用程序5秒后才终止。
            //如果backthread是后台线程,则应用程序立即终止。
            console.writeline("it's from main thread.");
            //console.read();
        }

        private static void worker()
        {
            //休息5秒
            thread.sleep(5000);
            console.writeline("it's from worker thread.");
        }

    假如保留isbackground = true;但又想继续执行worker()方法的话,可以调用thread.join()方法来实现。join()方法能保证主线程(前台线程)在异步线程

thread(后台线程)运行结束后才会运行。

        static void main(string[] args)
        {
            threadstatuschange();
        }

        /// <summary>
        /// 线程状态之间的转换
        /// </summary>
        private static void threadstatuschange()
        {
            //创建一个新线程(默认为前台线程)
            thread backthread = new thread(worker)
            {
                //将线程更改为后台线程
                isbackground = true
            };

            //通过start方法启动线程
            backthread.start();

            //join()方法能保证主线程(前台线程)在异步线程thread(后台线程)运行结束后才会运行
            backthread.join();

            console.writeline("it's from main thread.");
            console.read();
        }

        private static void worker()
        {
            //休息5秒
            thread.sleep(5000);
            console.writeline("it's from worker thread.");
        }

    运行结果如下:

C#线程学习笔记一:线程基础

    四、 suspend和resume方法

    这两个方法在.net framework 1.0的时候就支持的方法,他们分别可以挂起线程及恢复挂起的线程,但在.net framework 2.0以后的版本中这两个方法都过时了。

    msdn的解释是这样:

    警告:

    不要使用suspend和resume方法来同步线程的活动。您无法知道挂起线程时它正在执行什么代码。如果您在安全权限评估期间挂起持有锁的线程,

则appdomain中的其他线程可能被阻止。如果您在线程正在执行类构造函数时挂起它,则 appdomain中尝试使用该类的其他线程将被阻止。这样很容易发生死锁。

        static void main(string[] args)
        {
            threadresume();
        }

        /// <summary>
        /// 线程恢复
        /// </summary>
        private static void threadresume()
        {
            thread thread = new thread(threadsuspend);
            thread.name = "thread1";
            thread.start();
            thread.sleep(2000);
            console.writeline("main thread is running.");
            //线程恢复
            thread.resume();
            console.read();
        }

        /// <summary>
        /// 线程挂起
        /// </summary>
        private static void threadsuspend()
        {
            console.writeline("thread: {0} has been suspended.", thread.currentthread.name);
            //将当前线程挂起
            thread.currentthread.suspend();
            console.writeline("thread: {0} has been resumed.", thread.currentthread.name);
        }

    在上面这段代码中thread1线程是在主线程中恢复的,但当主线程发生异常时,这时候thread1就会一直处于挂起状态,此时thread1所使用的资源就不能释放

(除非强制终止进程),当其它的线程需要使用这快资源的时候, 很有可能就会发生死锁现象。

    上面一段代码还存在一个隐患,假如把thread.sleep(2000);这段代码注释一下:

        static void main(string[] args)
        {
            threadresume();
        }

        /// <summary>
        /// 线程恢复
        /// </summary>
        private static void threadresume()
        {
            thread thread = new thread(threadsuspend);
            thread.name = "thread1";
            thread.start();
            //thread.sleep(2000);
            console.writeline("main thread is running.");
            //线程恢复
            thread.resume();
            console.read();
        }

        /// <summary>
        /// 线程挂起
        /// </summary>
        private static void threadsuspend()
        {
            console.writeline("thread: {0} has been suspended.", thread.currentthread.name);
            //将当前线程挂起
            thread.currentthread.suspend();
            console.writeline("thread: {0} has been resumed.", thread.currentthread.name);
        }

    这个时候,主线程因为跑(运行)得太快,做完自己的事情去唤醒thread1时,此时thread1还没有挂起,而此时唤醒thread1就会出现异常了。

    五、简单线程的使用

    其实在上面介绍前台线程和后台线程的时候已经通过threadstart委托创建一个线程了,此时已经实现了一个多线程的一个过程。

    下面通过parameterizedthreadstart委托的方式来实现多线程:

        static void main(string[] args)
        {
            threadtypeuseparameterized();
        }

        /// <summary>
        /// 前台线程与后台线程(使用parameterizedthreadstart委托的方式来实现多线程)
        /// </summary>
        private static void threadtypeuseparameterized()
        {
            //创建一个新线程(默认为前台线程)
            thread backthread = new thread(new parameterizedthreadstart(worker1));

            //通过start方法启动线程
            backthread.start(123);

            //如果backthread是前台线程,则应用程序5秒后才终止。
            //如果backthread是后台线程,则应用程序立即终止。
            console.writeline("it's from main thread.");
        }

        private static void worker1(object parameter)
        {
            //休息5秒
            thread.sleep(5000);
            console.writeline(parameter+" is from worker1 thread.");
            console.read();
        }

    运行结果如下:

C#线程学习笔记一:线程基础