一、线程 Thread ThreadPool

  线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数;多线程的意义在于一个应用程序中,有多个执行部分可以同时执行,一个进程中可以同时创建多个线程同时执行;对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行;这样主线程就不会阻塞,可以继续往下执行;等到后台线程执行完毕,再通知主线程,然后做出对应操作。

1、线程分前台线程和后台线程

  前台线程:应用程序必须运行完所有的前台线程才可以退出。

  应用程序的主线程以及使用Thread构造的线程都默认为前台线程;主线程执行完毕后,会等待所有子线程(前台)执行完毕后,才退出程序;前台线程可以修改为后台线程,方式是设置Thread.IsBackground 属性。

  后台线程:应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。

  主线程执行完毕后,直接退出程序,不论子线程(后台线程)是否执行完毕,线程池线程都是后台线程,Task工厂创建的线程也是后台线程;后台线程不会阻止进程的终止。属于某个进程的所有前台线程都终止后,该进程就会被终止;所有剩余的后台线程都会停止且不会完成。

2、前后台线程使用场景

  后台线程非常适合于完成后台任务,应该将被动侦听活动的线程设置为后台线程,而将负责发送数据的线程设置为前台线程,这样,在所有的数据发送完毕之前该线程不会被终止。一般前台线程用于需要长时间等待的任务,比如监听客户端的请求;后台线程一般用于处理时间较短的任务,比如处理客户端发过来的请求信息。

3、线程调度 优先级  

  在windows上执行的线程在执行了一定时间(一个时间片,一般30ms)后,windows将会进行“调度”,让其他线程占用CPU执行,而线程优先级来确定调度顺序。也就是说如果存在一个优先级是25的线程能够执行,那么windows将不会调用优先级为24的线程。而windows是一种“抢占式”的操作系统(操作系统将定期的中断当前正在执行的线程,将CPU分配给在等待队列的下一个线程),如果一个具有较高优先级的线程准备好运行,并且当前运行的是较低优先级的线程,windows将迫使较低优先级线程停止运行,开始运行较高优先级的线程。

  由于windows上线程调用是通过线程的优先级来实现的,如果想让一个应用程序能够被尽量多的调度,就需要设置线程的优先级, 可以通过Thread的Priority属性设置,以影响线程的基本优先级。Priority属性需要一个ThreadPriority枚举定义的值。

Highest > AboveNormal >  Normal >  BelowNormal > Lowest

  线程池线程的优先级默认为Normal,Thread创建的线程也是Normal

4、Thread

  创建和销毁线程(Thread)是一个要耗费大量时间的过程,另外,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能,为了改善这样的问题 引入了线程池。

5、ThreadPool

  线程池形象的表示就是存放应用程序中使用的线程的一个集合。CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列,当应用程序想执行一个异步操作时,就调用一个方法,就将一个任务放到线程池的队列中,线程池中代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

  ThreadPool线程分Worker线程和IO线程

  Worker线程:用来完成一些计算的任务,需要CPU不间断地处理,CPU和线程的资源是充分利用的。QueueUserWorkItem方法一般会使用工作者线程。

  IO线程:主要用来完成输入和输出的工作。输入输出操作分成三个步骤:启动、实际输入输出、处理结果。用于实际输入输出可由硬件(IO设备)完成,并不需要CPU的参与,正在运行的线程将处于等待状态,只有等任务完成后才会有事可做, 这样就造成线程资源浪费的问题,异步编程,就是在此处进行动作,让线程不等待,直接回到线程池,而启动和处理结果使用IO线程,而且它们可以在不同一个IO线程上。

  不适合线程池的场景:

  (1)、线程池内的所有线程都是默认Normal优先级,要调整线程的优先级,不要使用线程池线程。

  (2)、如果任务执行的时间比较长的话,建议还是自己开线程,因为有可能阻塞了线程池里面的线程最终导致线程池的线程被耗光。

  (3)、如果任务是要马上执行的,建议还是不要使用线程池,因为往线程池提交的任务都需要排队。

  注意:手动创建的Thread和线程池里的线程没有任何关系。

6、Thread ThreadPool 使用推荐

  推荐使用线程池线程而非新建线程(Thread)。因为就算只是单纯的新建一个线程,这个线程什么事情也不做,都大约需要1M的内存空间来存储执行上下文数据结构,并且线程的创建与回收也需要消耗资源,耗费时间。而线程池的优势在于线程池中的线程是根据需要创建与销毁,是最优的存在。

  然而,线程池线程是后台线程,主线程执行完毕后,不会等待后台线程而直接结束程序。.NET Framework4.0提供的Task,可以解决此类问题。

二、Task

  ThreadPool优于Thread,但是线程池也有以下缺点:

  (1)、没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成是获得一个返回值

  (2)、不支持线程的取消、完成、失败通知等交互性操作

  (3)、不支持线程执行的先后次序,如上边,主线程不会等待后台线程池线程执行结束而直接结束。

  .NET Framework4.0提供的Task在线程池的基础上进行了优化,并提供了更多的API,能解决上述缺点,编写多线程程序,Task已经优于传统的Thread ThreadPool方式。

  Task是更上层的封装,底部还是通过Thread或者ThreadPool实现的。

1、Task性能更优于ThreadPool

  1)ThreadPool的代码将以先进先出的算法存储在全局队列中,并且多个工作者线程之间竞争同一个同步锁。(这就Task性能优于ThreadPool的第一个原因)

  2)Task的代码将以先进后出的算法存储在本地队列中,工作者线程执行本地队列中的代码没有同步锁的限制(这是Task性能优于ThreadPool的第二个原因),并且当工作者线程2空闲并且工作者线程1忙碌时,工作者线程2会尝试从工作者线程1(或者别的忙碌的工作者线程)的本地队列尾部“偷”任务,并会获取一个同步锁,不过这种行为很少发生。

  

2、Task.Wait

  同于Thread.Join()方法,主线程上等待另一线程执行完成,可以设置时间,在这个时间没到或另外一个线程没执行完之前,当前线程会等待。另外,Task.Result会调用Wait方法。调用Wait或者Result可以确保代码捕获到异常,并从异常中恢复。

3、Task.FromResult

  创建一个带返回值的、已完成的Task(获取该Task后,直接Result,否则,Task都要run后才能获取结果)

  场景1:以同步的方式实现一个异步接口方法

  一个接口包含异步方法

interface IMyInterface
{
Task<int> DoSthAsync();
}

  以同步的方式实现该接口方法DoSthAsync,但要返回异步的结果,没有使用async/await

  实现类MyClass的DoSthAsync方法中,都是以同步方式实现,但返回结果要是Task<int>,使用Task.FromResult刚好能返回一个带值的异步结果。

public class MyClass : IMyInterface
{
public Task<int> DoSthAsync()
{
int result = ;
return Task.FromResult(result);
}
}

  场景2:从缓存中获取值,以同步或者异步的方式实现

  需要根据key从缓存中获取值,如果每个key对应的缓存存在,就直接中缓存中获取值,即同步方式获取一部结果(FromResult),如果不存在,就需要以异步的方式获取缓存。

  异步获取缓存的方法(async /await):

  

private async Task<string> GetValueAsync(int key)
{
  //可能是访问数据库(IO)
string result = await SomeAsyncMethod();
cache.TrySetValye(key, result);
return result;
}

  以下方法来获取缓存中的值,有可能是异步方式,也有可能是同步的方式(从本地缓存中获取,FromResult)

//返回的都是异步结果Task<>
public Task<string> GetValueFromCache(int key)
{
string result = string.Empty;
//本地缓存存在,就以同步方式获取一部结果(FromResult方法)
if(cache.TryGetValue(key, out result))
{
return Task.FromResult(result);
}
   //缓存中不存在,就通过异步方法获取
return GetValueAsync(key);
}

  注意,这个方法没有使用async/await,返回值是Task,而且直接调用一部方法GetValueAsync,其里边使用了async/await,相当于是同步调用了一部。

  另外,如果使用Task.FromResult不带返回值,就使用Task.FromResult(0) 或 Task.FromResult(null)。

异步多线程 Thread ThreadPool Task的更多相关文章

  1. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

  2. 异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

    1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 th ...

  3. .net 多线程 Thread ThreadPool Task

    先准备一个耗时方法 /// <summary>/// 耗时方法/// </summary>/// <param name="name">< ...

  4. .NET多线程(Thread,ThreadPool,Task,Async与Await)

    .NET多线程是什么? 进程与线程 进程是一种正在执行的程序. 线程是程序中的一个执行流. 多线程是指一个程序中可以同时运行多个不同的线程来执行不同的任务. .NET中的线程 Thread是创建和控制 ...

  5. 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读

    记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...

  6. Thread,ThreadPool,Task, 到async await 的基本使用方法和理解

    很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...

  7. .Net进阶系列(12)-异步多线程(Thread和ThreadPool)(被替换)

    一. Thread多线程   1. 两种使用方式 通过F12查看Thread后,发现有两类构造函数,ParameterizedThreadStart和ThreadStart,其中 ThreadStar ...

  8. Thread,ThreadPool,Task

    线程分为前台和后台.比如我们直接new一个Thread这就是前台线程. 前台线程一定会执行. 比如我们创建2个线程:1号,2号,同时执行,假设1号是主线程,1执行完了,依旧会等待2执行完成,整个程序才 ...

  9. 浅析C#中的Thread ThreadPool Task和async/await

    .net 项目中不可避免地要与线程打交道,目的都是实现异步.并发.从最开始的new Thread()入门,到后来的Task.Run(),如今在使用async/await的时候却有很多疑问. 先来看一段 ...

随机推荐

  1. POJ 2762 Going from u to v or from v to u?- Tarjan

    Description 判断一个有向图是否对于任意两点 $x$,  $y$ 都有一条路径使$x - >y$或 $y - >x$ Solution 对于一个强联通分量内的点 都是可以互相到达 ...

  2. LibreOJ #6013. 「网络流 24 题」负载平衡 最小费用最大流 供应平衡问题

    #6013. 「网络流 24 题」负载平衡 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   题目描述 ...

  3. 高负载PHP调优

    高负载PHP调优 针对PHP的Linux调优 调整文件描述符限制 # ulimit -n 1000000 # vi /etc/security/limits.conf # Setting Shell  ...

  4. MVVM模式理解

    MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自 ...

  5. IOS初级:导航控制器

    1.AppDelegate.m老生常谈了,创建window,创建根视图rootViewController - (BOOL)application:(UIApplication *)applicati ...

  6. 很漂亮的IP头格式

    IP数据包格式 TCP/IP协议定义了一个在因特网上传输的包,称为IP数据报(IP Datagram).这是一个与硬件无关的虚拟包,由首部和数据两部分组成.首部的前一部分是固定长度,共 20 字节,是 ...

  7. 【Linux】percona-toolkit工具包的安装

    一.检查和安装与Perl相关的模块 PT工具是使用Perl语言编写和执行的,所以需要系统中有Perl环境. 依赖包检查命令为: rpm -qa perl-DBI perl-DBD-MySQL perl ...

  8. 【Web】前端文件上传,带进度条

    最近做项目发现,在文件上传的过程中,增加进度条,能大大改善用户体验.本例介绍带进度条的文件上传 环境搭建 参考:[Java]JavaWeb文件上传和下载. 原生ajax上传带进度条 <%@ pa ...

  9. curl命令整理

    ##curl命令 curl命令是一个功能强大的网络工具,它能够通过http.ftp等方式下载文件,也能够上传文件. #####1. 下载单个文件,默认将输出打印到标准输出中(STDOUT)中``` c ...

  10. UOJ 117 欧拉回路(套圈法+欧拉回路路径输出+骚操作)

    题目链接:http://uoj.ac/problem/117 题目大意: 解题思路:先判断度数: 若G为有向图,欧拉回路的点的出度等于入度. 若G为无向图,欧拉回路的点的度数位偶数. 然后判断连通性, ...