本笔记摘抄自: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中线程分为前台线程和后台线程:

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

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

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

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

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

        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();
Console.WriteLine("It's from worker thread.");
}

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

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

注1:一个线程在执行的过程中,可能调用另一个线程,前者可以称为调用线程,后者成为被调用线程。

注2:Thread.Join()方法的使用场景:调用线程挂起,等待被调用线程执行完毕后,继续执行。

注3:被调用线程执行Join方法,告诉调用线程,你先暂停,我执行完了,你再执行。从而保证了先后关系。

        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();
Console.WriteLine("It's from worker thread.");
}

运行结果如下:

四、 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)
{
Name = "Thread1"
};
thread.Start();
Thread.Sleep();
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)
{
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就会出现异常了。

    五、Abort和Interrupt方法

Abort方法和Interrupt都是用来终止线程的,但是两者还是有区别的:

1、它们抛出的异常不一样:Abort 方法抛出的异常是ThreadAbortException,Interrupt抛出的异常为ThreadInterruptedException。

2、调用Interrupt方法的线程之后可以被唤醒,然而调用Abort方法的线程就直接被终止不能被唤醒了。

下面演示Abort方法的使用:

        static void Main(string[] args)
{
//ThreadType();
//ThreadStatusChange();
//ThreadResume();
ThreadAbort();
} /// <summary>
/// 线程中断(不可再唤醒)
/// </summary>
private static void ThreadAbort()
{
Thread threadAbort = new Thread(AbortMethod)
{
Name = "ThreadAbort"
};
threadAbort.Start();
Thread.Sleep();
try
{
threadAbort.Abort();
}
catch
{
Console.WriteLine("1-> {0} exception happen in main thread.", Thread.CurrentThread.Name);
Console.WriteLine("2-> {0} status is:{1} in main thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
}
finally
{
Console.WriteLine("3-> {0} status is:{1} in main thread.", threadAbort.Name, threadAbort.ThreadState);
}
threadAbort.Join();
Console.WriteLine("4-> {0} status is:{1}", threadAbort.Name, threadAbort.ThreadState);
Console.Read();
} /// <summary>
/// Abort方法
/// </summary>
private static void AbortMethod()
{
try
{
Thread.Sleep();
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
Console.WriteLine("5-> {0} exception happen in abort thread.", Thread.CurrentThread.Name);
Console.WriteLine("6-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
}
finally
{
Console.WriteLine("7-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
}
}

运行结果如下:

从运行结果可以看出,调用Abort方法的线程引发的异常类型为ThreadAbortException,另外异常只会在调用Abort方法的线程中发生,而不会在主线程中抛出,

其次调用Abort方法后线程的状态不是立即改变为Aborted状态,而是从AbortRequested->Aborted。

下面演示Interrupt方法的使用:

        static void Main(string[] args)
{
ThreadInterrupt();
} /// <summary>
/// 线程中断(可再唤醒)
/// </summary>
static void ThreadInterrupt()
{
Thread threadInterrupt = new Thread(InterruptMethod)
{
Name = "ThreadInterrupt"
};
threadInterrupt.Start();
threadInterrupt.Interrupt();
threadInterrupt.Join();
Console.WriteLine("1-> {0} status is:{1} ", threadInterrupt.Name, threadInterrupt.ThreadState);
Console.Read();
}      
/// <summary>
/// Interrupt方法
/// </summary>
private static void InterruptMethod()
{
try
{
Thread.Sleep();
}
catch (Exception e)
{
Console.WriteLine(e.GetType().Name);
Console.WriteLine("2-> {0} exception happen in interrupt thread.", Thread.CurrentThread.Name);
Console.WriteLine("3-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
}
finally
{
Console.WriteLine("4-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
}
}

运行结果如下:

从结果中可以得到,调用Interrupt方法抛出的异常为:ThreadInterruptException, 另外当调用Interrupt方法后线程的状态应该是中断的,但是从运行结果看,

此时的线程因为Join、Sleep方法而唤醒了线程。

为了进一步解释调用Interrupt方法的线程可以被唤醒, 我们可以在线程执行的方法中运用循环,如果线程可以唤醒,则输出结果中就一定会有循环的部分,

而调用Abort方法的线程则不会有循环的部分。

下面代码相信大家看后肯定会更加理解两个方法的区别:

        static void Main(string[] args)
{
//ThreadType();
//ThreadStatusChange();
//ThreadResume();
//ThreadAbort();
//ThreadInterrupt();
ThreadWake();
} /// <summary>
/// 线程唤醒
/// </summary>
static void ThreadWake()
{
Thread threadWake = new Thread(WakeMethod);
threadWake.Start();
Thread.Sleep(); threadWake.Interrupt();
Thread.Sleep();
Console.WriteLine("1-> After finally block,the threadWake status is:{0}", threadWake.ThreadState);
Console.Read();
} /// <summary>
/// Wake方法
/// </summary>
private static void WakeMethod()
{
for (int i = ; i < ; i++)
{
try
{
Thread.Sleep();
Console.WriteLine("2-> Thread is Running.");
}
catch (Exception ex)
{
if (ex != null)
{
Console.WriteLine("3-> Exception {0} throw.", ex.GetType().Name);
}
}
finally
{
Console.WriteLine("4-> Current thread status is:{0}", Thread.CurrentThread.ThreadState);
}
}
}

运行结果如下:

如果把上面的threadWake.Interrupt();改为threadWake.Abort(); 运行结果为:

    六、简单线程的使用

其实在上面介绍前台线程和后台线程的时候已经通过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(); //如果backThread是前台线程,则应用程序5秒后才终止。
//如果backThread是后台线程,则应用程序立即终止。
Console.WriteLine("It's from main thread.");
} private static void Worker1(object parameter)
{
//休息5秒
Thread.Sleep();
Console.WriteLine(parameter+" is from worker1 thread.");
Console.Read();
}

运行结果如下:

C#线程学习笔记一:线程基础的更多相关文章

  1. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  2. C#线程学习笔记九:async & await入门二

    一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...

  3. JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析

    JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...

  4. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  5. Linux进程线程学习笔记:运行新程序

    Linux进程线程学习笔记:运行新程序                                         周银辉 在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下 ...

  6. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  7. Java学习笔记:语言基础

    Java学习笔记:语言基础 2014-1-31   最近开始学习Java,目的倒不在于想深入的掌握Java开发,而是想了解Java的基本语法,可以阅读Java源代码,从而拓展一些知识面.同时为学习An ...

  8. 卷积神经网络(CNN)学习笔记1:基础入门

    卷积神经网络(CNN)学习笔记1:基础入门 Posted on 2016-03-01   |   In Machine Learning  |   9 Comments  |   14935  Vie ...

  9. 「学习笔记」字符串基础:Hash,KMP与Trie

    「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...

  10. java学习笔记14--多线程编程基础1

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...

随机推荐

  1. hdu3999 The order of a Tree

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3999 题意:给一序列,按该序列插入二叉树,给出字典序最小的插入方法建相同的一棵树出来.即求二叉树的先序 ...

  2. HDU5394 Bomb

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5934 There are NN bombs needing exploding. Each bomb ha ...

  3. HDU-6114

    車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子.一天,小度在棋盘上摆起了许多車--他想知道,在一共N×M个点的矩形棋盘中摆最多个数的車使其互不攻击的方案数.他经过思考,得出了 ...

  4. Java语法进阶14-网络编程

    网络编程 软件结构 C/S结构 :全称为Client/Server结构,是指客户端和服务器结构. B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构. 网络通信协议 网络通信 ...

  5. Django开发登录功能实战

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:凡夫俗子66 Django 如果是定义函数写登录路由,需要判断请求方法 ...

  6. 用Python写一个游戏脚本,你会吗?

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:ivat4u  学习python有一段时间了,由于python语言的强大 ...

  7. 基于iCamera测试AR0134 960p 全局快门相机模块小结

    基于iCamera测试AR0134 960p 全局快门相机模块小结 首先看看此模块的特性 AR0134 全局曝光 CMOS模块 1280*960像素 5.3 V/lux-sec 摄像头模块实物靓照(上 ...

  8. Nginx(三)--Nginx 的高可用

    1. Nginx 进程模型简介 多进程+多路复用 master 进程 . worker 进程  启动nginx,查看nginx的线程,可以发现: [root@localhost ~]# ps -ef ...

  9. docker入门-镜像管理命令篇

    一.下载.上传镜像   1:下载安装centos镜像 语法:docker [参数][镜像名称] [root@host1 ~]# docker pull centos Using default tag ...

  10. 深入学习CSS3-flexbox布局

    学习博客:https://css-tricks.com/snippets/css/a-guide-to-flexbox/ 学习博客:http://caibaojian.com/demo/flexbox ...