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中的线程
一.程序.进程和线程 程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...
随机推荐
- php与web页面交互(二)
一.获取表单数据 1.1 使用POST()方法提交表单 ---POST()方法可以没有限制地传递数据到服务器,所提交的数据在后台传输,用户在浏览器端是看不到这一过程的,安全性高,适用于发送保密数据和 ...
- MacbookPro管理员问题
更改用户名重启之后,发现用户名还是用户名,管理员权限降成了普通用户. 参考这个帖子改的用户名: https://zhidao.baidu.com/question/259845860.html 找回管 ...
- ftp服务器的简单配置使用
yum install -y vsftpd systemctl start vsftpd cd /var/ftp/pub/ mkdir 111 touch weifeng.txt ...
- [HNOI2008] GT考试
[HNOI2008] GT考试 标签 : DP 矩阵乘法 题目链接 题意 n位数中不出现一个子串的方案数. 题解 \(设dp[i][j]\)为前i位匹配到j时的合法方案数.(所谓合法,就是不能有别的匹 ...
- 【JavaWeb】权限管理系统
前言 前面我们做的小项目都是一个表的,业务代码也相对简单.现在我们来做一个权限管理系统,体验一下多表的业务逻辑,顺便巩固一下过滤器的知识.! 目的 现在我有一个管理商品.订单的页面.当用户点击某个超链 ...
- springmvc log4j 配置
web.xml 增加 <context-param> <param-name>log4jConfigLocation</param-name> <param- ...
- 二、Item Pipeline和Spider-----基于scrapy取校花网的信息
Item Pipeline 当Item在Spider中被收集之后,它将会被传递到Item Pipeline,这些Item Pipeline组件按定义的顺序处理Item. 每个Item Pipeline ...
- spring 完成multipart数据上传
spring中多媒体文件的上传,通过配置 MultipartResolver 实现. MultipartResolver 的实现类有两个:一.StandardServletMultipartResol ...
- 对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半。 经过若干次这样的变换,一定会使n变为1
对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半.经过若干次这样的变换,一定会使n变为1.例如,3→10→5→16→8→4→2→1.输入n,输出变换的次数.n≤10 9 .样 ...
- maven中的profile文件的解析
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...