新开了一个多线程编程系列,该系列主要讲解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的更多相关文章

  1. 多线程编程-- part 2 线程的生命周期和优先级

    线程的创建到消亡的历程: java多线程的5种状态: (1)New(新建) new Thread(run()) 该线程还没开始运行,状态是new,在程序运行前还有一些基础工作要做 (2)runnabl ...

  2. iOS多线程编程之创建线程安全(转载)

    一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数 ...

  3. 七. 多线程编程2.Java线程模型

    Java运行系统在很多方面依赖于线程,所有的类库设计都考虑到多线程.实际上,Java使用线程来使整个环境异步.这有利于通过防止CPU循环的浪费来减少无效部分. 为更好的理解多线程环境的优势可以将它与它 ...

  4. java多线程编程(二创建线程)

    1.概念           因为java是完全面向对象的,所以在java中,我们说的线程,就是Thread类的一个实例对象.所以,一个线程就是一个对象,它有自己字段和方法. 2.创建线程 创建线程有 ...

  5. UNIX环境编程学习笔记(28)——多线程编程(三):线程的取消

    lienhua342014-11-24 1 取消线程 pthread 提供了pthread_cancel 函数用于请求取消同一进程中的其他线程. #include <pthread.h> ...

  6. iOS多线程编程之创建线程(转载)

    一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 (1)创建.启动线程 NSThread *thread = [[NSThread alloc] initWithTarget:sel ...

  7. 多线程编程-- part 4 线程间的通信

    线程间的相互作用 线程之间需要一些协调通信,来共同完成一件任务. Object类相关的方法:notify(),notifyAll(),wait().会被所有的类继承,这些方法是final不能被重写.他 ...

  8. Java多线程编程之守护线程

    Java的线程分为两种,一个是用户线程,一个是守护线程.守护线程守护的对象就是用户线程,当用户线程结束后,守护它的守护线程也就会结束.二者的本质基本是一样的,唯一区别在于何时结束. 用户线程:直到自己 ...

  9. java核心-多线程(6)-线程池-ThreadPoolExecutor

    1.java多线程编程少不了使用线程池,线程池相关的工具类所在jdk包,java.util.concurrent 2.使用示例 demo1 public class ThreadPoolDemo { ...

  10. Java多线程编程(1)--Java中的线程

    一.程序.进程和线程   程序是一组指令的有序集合,也可以将其通俗地理解为若干行代码.它本身没有任何运行的含义,它只是一个静态的实体,它可能只是一个单纯的文本文件,也有可能是经过编译之后生成的可执行文 ...

随机推荐

  1. 【IT人】如何提高阅读源代码的效率

    1.最近刚到公司,公司就发一架构代码自己看,看了几天看的想吐,也在网上找了下相关的技巧吧,不是有句话叫做:成功必有方法,失败总是借口! 2.借鉴别人的方法来看看如下: 记得在开源流行之前,我看过的代码 ...

  2. AMD && CMD

    前言 JavaScript初衷:实现简单的页面交互逻辑,寥寥数语即可: 随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀 问题: 这时候JavaSc ...

  3. 构建企业级Nginx+Keepalived集群架构

    随着Nginx在国内的发展潮流,越来越多的互联网公司都在使用Nginx. Nginx高性能.稳定性成为IT人士青睐的http和反向代理服务器,今天我们来实战构建Nginx+Keepalived高可用架 ...

  4. Ajax简单总结

    Ajax=异步JS和XML: 主要是局部的数据更新,即不需要刷新整个页面: 首先,需要新建一个XMLHttpRequest对象[这里注意如果是ie7以下的就是创建ActiveXObject]: var ...

  5. 4、flask之分页插件的使用、添加后保留原url搜索条件、单例模式

    本篇导航: flask实现分页 添加后保留原url搜索条件 单例模式 一.flask实现分页 1.django项目中写过的分页组件 from urllib.parse import urlencode ...

  6. CentOS利用Nginx+Docker部署.netcore应用

    安装docker 官方文档https://docs.docker.com/engine/installation/linux/docker-ce/centos/ [root@sn ~]# yum re ...

  7. 【剑指offer28:字符串的排列】【java】

    题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. import ja ...

  8. Hibernate 一对一中的一些问题

    1.对于想查询一对一种一方为空的时候使用 例如一个用户对应一个人,则要从人查找没有用户的人员的话, 使用hql语句是查询不到的 我今天也碰到了这个问题,研究了下,可以用以下语句查出来:from Per ...

  9. 对于ArrayList中的泛型进行分析

    package cn.lonecloud.reflect; import java.lang.reflect.Method; import java.util.ArrayList; public cl ...

  10. ListIterator的使用

    package cn.lonecloud.Iterator; import java.util.ArrayList; import java.util.ListIterator; public cla ...