C#多线程编程(1)--线程,线程池和Task
新开了一个多线程编程系列,该系列主要讲解C#中的多线程编程。 利用多线程的目的有2个: 一是防止UI线程被耗时的程序占用,导致界面卡顿;二是能够利用多核CPU的资源,提高运行效率。
我没有进行很深入的讲解,是以实际使用为主。我的这个系列主要是《CLR via C#》的总结,该书的作者Jeffrey Richter是C#的顾问,他本人对windows见解极深。尤其是多线程部分,书中讲解的非常透彻,文中讲解不到或者你想要更深入的了解的同学,可以找来《CLR via C#》仔细研究。
当一个会执行很长时间的程序,如从服务端获取数据,当该程序执行过程中,客户端一直处于等待状态,等待该程序执行完成,然后再执行其他代码。若是UI程序,用户会感到界面卡顿,影响使用体验。我们希望这样卡顿的程序能够“偷偷”在后台跑,不要影响到界面。解决这个问题就要使用多线程,其中一部分线程负责响应界面操作,另一部分线程负责后台计算。代码如下:
public void GetData()
{
var thread = new Thread(() => LoadDataFromServer());
thread.start();
}
public void LoadDataFromServer(){
//模拟数据读取
Thread.Sleep(2000);
Console.WriteLine("读取完成。");
}
thread就是你创建的线程,然后调用Start()方法,该线程就会开始执行,LoadDataFromServer()是你想要执行的方法,这里是从服务读取数据,Windows会负责调度这个线程,决定这个线程什么时候开始执行。这样就可以做到新线程负责读取数据,主线程不等待,继续执行,界面不卡顿。这样做很好,因为做到了异步,界面很流畅,但是这不是最优解。当程序执行很长时间,每一次从服务端读取数据,为了不造成界面卡顿,就要新创建个线程。当数据加载完成后,新线程就没用了。创建一个线程开销很大(具体开销就不介绍了,感兴趣的可以上网查相关资料,《Clr via C#》中有很详细的介绍),如果每一次被创建的线程在运行结束后,不被释放,而是存起来,留下一次使用,这样是不是就可以节省资源?线程池就是干这个的,例子如下:
//一些操作
ThreadPool.QueueUserWorkItem(()=>LoadDataFromServer());
//其他操作
可以看到,上段代码没有显式创建线程,而是把方法放到了ThreadPool.QueueUserWorkItem()方法中,ThreadPool负责创建和管理线程。当程序刚开始时,ThreadPool第一次被调用,这时线程池里一个线程没有,线程池会创建一个新线程,当程序再次调用线程池时,若线程池忠还有空闲线程,则直接调用空闲线程执行程序;若程序调用线程池时,线程池中没有空闲线程且CPU处于“未饱和”状态,则线程池会创建新线程。实际上,当调用线程池时,相当于把要执行的方法“挂”在线程池的任务队列上,当CPU处于“未饱和”状态,线程池就会调用线程来执行线程池任务队列中的任务。
ThreadPool.QueueUserWorkItem()方法有一个问题,那就是没有很便捷的方法获得方法的返回值,不知道LoadDataFromServer()方法何时执行完成。为了解决这个问题,C#引入了Task,和泛型Task<T>。代码如下
var data = Task.Run(() => LoadDataFromServer()).Result;
先讲解一下,Task.Run()是对ThreadPool.QueueUserWorkItem()方法的封装,该方法会返回Task,然后可以通过调用task.Result来获得LoadDataFromServer()的返回值。实际上这段代码并不会异步执行,原因是data所在的线程会等待LoadDataFromServer()的返回值,不然data会没有值,程序无法执行,所以此时线程被阻塞,知道任务完成,该线程才会继续执行。为了解决这一问题,C#引入了async 和 await 两个关键字。代码如下:
public async void LoadData(){
var data = await Task.Run(() => LoadDataFromServer());
Console.WriteLine(data);
}
public string LoadDataFromServer(){
//模拟到服务器读取数据
Thread.Sleep(2000);
return "Data";
}
C#规定只能在标有async的方法中使用await 关键字,该关键字会将await后面的代码编译成状态机,在LoadDataFromServer()方法执行结束后,程序会重新进入LoadData()方法,并从await处继续执行,该关键字不会阻塞线程(编译器如何将await的异步方法编译成状态机,《CLR via C#》28.4节有详细讲解)。
以上就是多线程编程的第一部分--Thread, ThreadPool和Task的讲解,下一节会继续讲解Task的其他特性与方法。
C#多线程编程(1)--线程,线程池和Task的更多相关文章
- 多线程编程-- part 2 线程的生命周期和优先级
线程的创建到消亡的历程: java多线程的5种状态: (1)New(新建) new Thread(run()) 该线程还没开始运行,状态是new,在程序运行前还有一些基础工作要做 (2)runnabl ...
- iOS多线程编程之创建线程安全(转载)
一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数 ...
- 七. 多线程编程2.Java线程模型
Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程.实际上,Java使用线程来使整个环境异步.这有利于通过防止CPU循环的浪费来减少无效部分. 为更好的理解多线程环境的优势可以将它与它 ...
- java多线程编程(二创建线程)
1.概念 因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...
- UNIX环境编程学习笔记(28)——多线程编程(三):线程的取消
lienhua342014-11-24 1 取消线程 pthread 提供了pthread_cancel 函数用于请求取消同一进程中的其他线程. #include <pthread.h> ...
- iOS多线程编程之创建线程(转载)
一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 (1)创建.启动线程 NSThread *thread = [[NSThread alloc] initWithTarget:sel ...
- 多线程编程-- part 4 线程间的通信
线程间的相互作用 线程之间需要一些协调通信,来共同完成一件任务. Object类相关的方法:notify(),notifyAll(),wait().会被所有的类继承,这些方法是final不能被重写.他 ...
- Java多线程编程之守护线程
Java的线程分为两种,一个是用户线程,一个是守护线程.守护线程守护的对象就是用户线程,当用户线程结束后,守护它的守护线程也就会结束.二者的本质基本是一样的,唯一区别在于何时结束. 用户线程:直到自己 ...
- java核心-多线程(6)-线程池-ThreadPoolExecutor
1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent 2.使用示例 demo1 public class ThreadPoolDemo { ...
- Java多线程编程(1)--Java中的线程
一.程序.进程和线程 程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...
随机推荐
- XSD详解三 - 复合元素+总结篇
一.复合元素介绍 1.什么是复合元素? 复合元素指包含其他元素及/或属性的 XML 元素. 有四种类型的复合元素: 空元素 包含其他元素的元素 仅包含文本的元素 包含元素和文本的元素 注释:上述元素均 ...
- centos java环境搭建
我个人配置的是阿里云centos 7.4 64bit 不存在openjdk 看下面内容的情况下看是否存在openjdk java -version 如果返回java版本值,则存在openjdk,需要卸 ...
- 损失函数 hinge loss vs softmax loss
1. 损失函数 损失函数(Loss function)是用来估量你模型的预测值 f(x) 与真实值 Y 的不一致程度,它是一个非负实值函数,通常用 L(Y,f(x)) 来表示. 损失函数越小,模型的鲁 ...
- Html5本地存储和本地数据库
一个网站如何能在客户的浏览器存储更多的数据呢? 在Html4的时代在浏览器端存储点网站个性化的数据,尤其是用户浏览器的痕迹,用户的相关数据等一般只能存储在Cookie中,但是大多是浏览器对于Cooki ...
- linux使用tcpdump抓包工具抓取网络数据包,多示例演示
tcpdump是linux命令行下常用的的一个抓包工具,记录一下平时常用的方式,测试机器系统是ubuntu 12.04. tcpdump的命令格式 tcpdump的参数众多,通过man tcpdump ...
- [bzoj3998][TJOI2015]弦论-后缀自动机
Brief Description 给定一个字符串, 您需要求出他的严格k小子串或非严格k小子串. Algorithm Design 考察使用后缀自动机. 首先原串建SAM, 然后如果考察每个状态代表 ...
- 终极解决方案:java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
报错信息 javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does ...
- 创建Maven web工程不能解析EL表达式的解决办法
在web.xml中讲头部改为: <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee&qu ...
- Nginx和php是怎么通信的?
先来看一下搭建好PHP运行环境的Nginx配置文件. 非常重要的就是 fastcgi_pass 指令了,这个指令用于指定 fpm 进程监听的地址,Nginx 会把所有的 php 请求翻译成 fastc ...
- NumPy学习_00 ndarray的创建
1.使用array()函数创建数组 参数可以为:单层或嵌套列表:嵌套元组或元组列表:元组或列表组成的列表 # 导入numpy库 import numpy as np # 由单层列表创建 a = np. ...