线程与同步异步
一、线程
1、什么是线程?什么是进程?两者有什么关系?
进程(Process):进程代表了操作系统上运行着的一个应用程序,每个进程都有自己独立的边界,进程与进程之间不能共享资源,一个进程可以包含一个或多个线程;
线程(Thread):线程是被操作系统调度的基本单元,同一进程内的所有线程共享内存和资源,并且一个线程可以对同一进程内的其他线程进行访问或结束等操作;
关系:它们是一个包含的关系,进程就像是线程的容器,且至少包含一个线程
为了更形象的理解该部分内容,可以参考阮一峰的 进程与线程的一个简单解释
2、系统是如何调用线程的?
抢占式调度:所有的线程都在被不停地快速切换运行,使得用户感觉所有的线程都在并行运行;
非抢占式调度:某个线程在运行时不会被操作系统强制暂停,它可以持续地运行直到运行告一段落并主动交出运行权;
通常情况下,一些系统级别的线程采用的是非抢占式调度,而普通线程采用的是抢占式调度
3、调用会不会存在问题?
对于单核CPU的操作系统来说,线程在不停的切换,而每次切换线程内的数据也在被不停的搬入搬出,会在一定程度上影响性能开销;多核CPU的操作系统则可以并行的运行多个线程,理论上性能会成倍的提高。所以衍生出了多线程操作的概念
4、.NET中常见的线程对象
4 .1 多线程操作之Thread对象
从.NET1.0开始,我们就可以就通过Thread对象创建、控制个线程;简单示例如下,我们创建了10个进程并调用,从结果上来看它们是多个线程并行运行的,且执行线程Id和结束的线程Id是可以对应上的;
4.2 多线程操作之ThreadPool对象
上面可以看到,每次我们需要调用线程进行操作,都需要手动创建一个线程对象,执行完成后再交由GC去销毁,一定程度上会影响性能开销,而且使用起来不是很方便,所以CLR提供了一个叫“线程池(ThreadPool)”的对象
线程池有以下特性:①当一个线程被使用完毕后并不会立刻被销毁,而是放入线程池中等待下一次使用;当应用程序需要一个新的线程时,就可以从线程池中直接获取一个已经存在的线程;②当线程池中的线程数小于线程池设置的下限时,线程池会创建新的线程;而当线程池中的线程数大于线程池设置的上限时,线程池将销毁多余的线程;
那么我们怎么操作线程池呢,ThreadPool对象提供了几个静态方法,我们使用一个简单的,示例如下,创建一个线程池后,做与上一个示例相同的操作,可以看到3号线程被重复调用了两次(随机事件)
4.3 多线程操作之Task对象
上面的示例可以看到,线程池的使用可以复用线程,一定程度上可以减少系统的开销。但是却有几个缺陷,比如:①不支持线程的挂起、取消等操作;②不支持线程的优先级设置;
所以在.NET4.0又出现了Task对象,它是基于线程池实现的,同时弥补了线程池功能上的一些不足,比如可以获取线程的状态,有完全的控制权等等,我们同样使用Task,做一个简单的示例,可以看到,任务2可以等待任务1执行完成后再执行自己的逻辑
4.4 多线程操作之Parallel对象
并行Paralle内部使用的是Task对象,它提供了Parallel.Invoke, Parallel.For, Parallel.Forecah 三个方法。需要注意的是所有并行任务完成后才会返回结果,所以少量短时间任务建议不要使用Parallel。通常情况下比较适合处理密集计算的场合。我没用过就不写例子了