.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. SqlServer 版本号

    RTM (no SP) SP1 SP2 SP3 SP4 SQL Server 2014      codename Hekaton  12.00.2000.8 SQL Server 2012      ...

  2. CSS学习第三天

    定位布局: 相对定位:相对于自身的位置进行偏移position需要搭配left right top bottom      position: relative; 绝对定位:相对于有position属 ...

  3. C#LeetCode刷题之#14-最长公共前缀​​​​​​​(Longest Common Prefix)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3921 访问. 编写一个函数来查找字符串数组中的最长公共前缀. 如 ...

  4. 代码优化与sql优化---未完待续

    万丈高楼平地起,还是得打一个好地基呀 减少对变量对重复计算 //一般这么写 for (int i = ; i < list.size(); i++) {...} //建议修改为: for (in ...

  5. Python Matplotlib绘图基础

    Matplotlib绘图基础 1.Figure和Subplot import numpy as np import matplotlib.pyplot as plt #创建一个Figure fig = ...

  6. 对java程序员来说时间格式永远让人挠头来看Java Date Time 教程-时间测量

    在Java中,用System.currentTimeMillis()来测量时间最方便. 你要做的是在某些操作之前获取到时间,然后在这些操作之后你想要测量时间,算出时间差.下面是一个例子: long s ...

  7. Gitlab-CI/CD 1

    Gitlab-Runner自动构建服务器搭建1 这里讲到的gitlab仓库指的是https://gitlab.com/,自建gitlab仓库也基本相同. 项目的构建打包过程相对比较消耗系统性能,所以g ...

  8. .Net Core中的诊断日志DiagnosticSource讲解

    前言     近期由于需要进行分布式链路跟踪系统的技术选型,所以一直在研究链路跟踪相关的框架.作为能在.Net Core中使用的APM,SkyWalking自然成为了首选.SkyAPM-dotnet是 ...

  9. 5 个 Git 工作流,改善你的开发流程

    原文地址:5 Git workflows you can use to deliver better code and improve your development process 原文作者:Vi ...

  10. idea创建mybatis的逆向工程:generator

    1在工程的目录下配置mybatis-generator插件 <build> <plugins> <!-- mybatis逆向工程 --> <plugin> ...