.NET多线程是什么?

进程与线程

进程是一种正在执行的程序。

线程是程序中的一个执行流。

多线程是指一个程序中可以同时运行多个不同的线程来执行不同的任务。

.NET中的线程

Thread是创建和控制线程的类。

ManagedThreadId是线程ID。

CurrentThread是获取当前正在运行的线程。

同步与异步

同步是调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。(单线程)

异步调用一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。(多线程)

.NET中的多线程发展

主要有Thread,ThreadPool,Task

Thread就是线程,需要自己调度,直接跟系统对接,相对管理比较复杂及效率差。

ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的。但是ThreadPool提供的接口比较少。

Task和ThreadPool是一样的,都是从线程池中取空闲的线程。比ThreadPool调用接口更加丰富。目前.Net使用多线程管理,应该优先使用Task。

代码:

/// <summary>
/// 多线程发展历史
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnHistory_Click(object sender, EventArgs e)
{
Console.WriteLine($"Thread start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
var threadStart = new ThreadStart(DoNothing);
var thread = new Thread(threadStart);
thread.Start();
Console.WriteLine($"Thread end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(3000);
Console.WriteLine($"ThreadPool start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
var callback = new WaitCallback(DoNothing);
ThreadPool.QueueUserWorkItem(callback);
Console.WriteLine($"ThreadPool end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); Thread.Sleep(3000);
Console.WriteLine($"Task start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Action action = DoNothing;
Task task = new Task(action);
task.Start();
Console.WriteLine($"Task end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}

为什么需要多线程?

特点:

  • 卡界面:单线程卡,多线程不卡
  • 性能好:单线程差,多线程好(资源换性能)
  • 执行顺序:单线程顺序,多线程无序

代码:

/// <summary>
/// 同步(单线程)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnSync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
for (int i = 0; i < 5; i++)
{
DoNothing();
}
Console.WriteLine($"btnSync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
} /// <summary>
/// 异步(多线程)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsync_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnAsync_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
for (int i = 0; i < 5; i++)
{
var ts = new ThreadStart(DoNothing);
var t = new Thread(ts);
t.Start();
}
Console.WriteLine($"btnAsync_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
} private void DoNothing()
{
Console.WriteLine($"DoNothing start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
Console.WriteLine($"DoNothing end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}

如何使用.NET多线程?

Task

创建任务

1、通过调用任务类构造函数实例化,但通过调用其Start()启动任务。

Task t1 = new Task(action, "alpha");
t1.Start();

2、通过调用TaskFactory.StartNew(Action ,Object)方法在单个方法调用中实例化和启动任务。

Task t2 = Task.Factory.StartNew(action, "beta");

3、通过调用Run(Action)方法在单个方法调用中实例化和启动任务。

Task t3 = Task.Run(action);

从任务中返回值

Result 属性将阻止调用线程,直到任务完成。

Task<int> task1 = Task<int>.Factory.StartNew(() => 1);
int i = task1.Result;

等待任务完成

可以通过调用 Wait 方法来等待一个或多个任务完成,从而同步调用线程的执行以及它启动的异步任务。

调用无参数 Wait() 方法以无条件等待,直到任务完成。

调用Wait(Int32)和 Wait(TimeSpan) 方法会阻止调用线程,直到任务完成或超时间隔(以先达到者为准)为止。

调用WaitAny(Task[])方法等待一组任务中第一个任务完成。

调用WaitAll(Task[])方法来等待一系列任务全部完成。

异常处理

调用代码可以通过使用 try/catch 块中的以下任意方法来处理异常:

  • await task
  • task.Wait()
  • task.Result
  • task.GetAwaiter().GetResult()

代码:

var task1 = Task.Run(() => { throw new Exception("This exception is expected!"); });
try
{
task1.Wait();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();

取消任务

你可以使用 CancellationTokenSource 类在以后某一时间发出取消请求。

static void Main(string[] args)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
var tasks = new ConcurrentBag<Task>(); Task tc;
for (int i = 0; i < 10; i++)
{
var k = i;
tc = Task.Run(() => DoNothing(k, token), token);
tasks.Add(tc);
} char ch = Console.ReadKey().KeyChar;
if (ch == 'c' || ch == 'C')
{
tokenSource.Cancel();
Console.WriteLine("\n开始取消任务.");
} try
{
Task.WhenAll(tasks.ToArray());
}
catch (OperationCanceledException)
{
Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n");
}
finally
{
tokenSource.Dispose();
}
Console.ReadKey();
} private static void DoNothing(int i, CancellationToken ct)
{
Console.WriteLine($"DoNothing start index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(i * 1000);
if (ct.IsCancellationRequested)
{
Console.WriteLine($"任务已取消 index:{i} id:{Thread.CurrentThread.ManagedThreadId} ");
ct.ThrowIfCancellationRequested();
}
Console.WriteLine($"DoNothing end index:{i} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}

实际案例

代码:

/// <summary>
/// Task实际案例
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTask_Click(object sender, EventArgs e)
{
Console.WriteLine($"项目组接到任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Console.WriteLine($"项目经理设计数据库,设计原型,分配任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
List<Task> tasks = new List<Task>(); tasks.Add(Task.Run(() => Coding("赵XX","前端页面")));
tasks.Add(Task.Run(() => Coding("王XX", "IOS页面")));
tasks.Add(Task.Run(() => Coding("黄XX", "后端接口")));
tasks.Add(Task.Run(() => Coding("杜XX", "后端接口"))); TaskFactory taskFactory = new TaskFactory();
taskFactory.ContinueWhenAll(tasks.ToArray(), t =>
{
Console.WriteLine($"项目经理发布,测试人员测试任务 id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); });
} private void Coding(string personName,string taskName)
{
Console.WriteLine($"{personName}开发{taskName} id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
}

async和await

Async 和 Await几乎与创建同步方法一样创建异步方法。

代码:

/// <summary>
/// Async和Await应用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsyncAndAwait_Click(object sender, EventArgs e)
{
Console.WriteLine($"btnAsyncAndAwait_Click start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
DoNothingAsync();
Console.WriteLine($"btnAsyncAndAwait_Click end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}"); } private async Task DoNothingAsync()
{
Console.WriteLine($"DoNothingAsync start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
await Task.Run(() => {
Console.WriteLine($"DoNothingAsync Task start id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
Thread.Sleep(2000);
Console.WriteLine($"DoNothingAsync Task end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
});
Console.WriteLine($"DoNothingAsync end id:{Thread.CurrentThread.ManagedThreadId} time:{DateTime.Now}");
}

参考

.NET多线程(Thread,ThreadPool,Task,Async与Await)的更多相关文章

  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

    一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...

  3. .net 多线程 Thread ThreadPool Task

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

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

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

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

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

  6. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

  7. 多线程编程学习笔记——async和await(三)

    接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多 ...

  8. Thread,ThreadPool,Task

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

  9. 多线程编程学习笔记——async和await(一)

    接上文 多线程编程学习笔记——任务并行库(一) 接上文 多线程编程学习笔记——任务并行库(二) 接上文 多线程编程学习笔记——任务并行库(三) 接上文 多线程编程学习笔记——任务并行库(四) 通过前面 ...

随机推荐

  1. C#LeetCode刷题之#191-位1的个数(Number of 1 Bits)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4052 访问. 编写一个函数,输入是一个无符号整数,返回其二进制表 ...

  2. Vue Vue-loader / VueLoaderPlugin / Webpack

    在不用VueCli创建项目的时候,手写引入vue的时候,配置webpack的时候发现了这个问题 webpack vue-loader was used without the correspondin ...

  3. DAPP开发初探——永存的留言

    转载地址 https://blog.csdn.net/qq_33764491/article/details/80570266 前言 最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多 ...

  4. mysql的ATM存取款机系统

    ##建库 CREATE DATABASE bankDB; ##客户信息表 CREATE TABLE userInfo ( customerID INT PRIMARY KEY AUTO_INCREME ...

  5. 火题小战 B. barbeque

    火题小战 B. barbeque 题目描述 \(Robbery\) 是一个大吃货(雾) 某个神奇的串由牛肉和青椒构成,于是\(Robbery\)购买了\(n\)个餐包来自己做这个串,每个餐包中有一些牛 ...

  6. 双系统Linux和win10系统时间不一样。

    https://jingyan.baidu.com/article/456c463b4e6a5a0a5831443a.html

  7. 汇编环境搭建错误VMware Workstation 不可恢复错误: (vcpu-0) vcpu-0:VERIFY vmcore/vmm/main/cpuid.c:386 bugNr=1036521

    一.错误 在使用VMware建立虚拟机时,出现 二.解决 在此界面不要选择MS-DOS

  8. Mybatis中List传值

    Mybatis遇到List传值时常用方法. 1. List<E>,E为基本数据类型和String Mapper类文件: List<User> selectByIds(List& ...

  9. python3 - selenium 添加有账号密码的代理

    from selenium import webdriver import string import zipfile # 打包Google代理插件 def create_proxyauth_exte ...

  10. Python基础入门知识点——深浅拷贝

    深浅拷贝 对象引用.浅拷贝.深拷贝(拓展.难点.重点) Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果 其实这个是由于共享内存导致的结果 拷贝 ...