.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# winform 弹出窗体给父窗体传值

    Winform程序有很多传值的方法,抱着学习的态度.利用委托注册事件的方法,给窗体统一添加事件: 首先定义一个Frm_Base: namespace 任意 { public partial class ...

  2. C#LeetCode刷题之#724-寻找数组的中心索引( Find Pivot Index)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3742 访问. 给定一个整数类型的数组 nums,请编写一个能够返 ...

  3. .NetCore(Avalonia) 项目dll混淆,Ubuntu 或者deepin操作系统 deb安装包解压,重新打包

    .NetCore(Avalonia) 项目dll混淆,deb安装包解压,重新打包 本文分为两部分,一部分是介绍使用 DotNetReactor6.0 及以上版本混淆.netcore项目的dll. 另一 ...

  4. 代码备忘录--常用的一些Doxygen格式

    1.文件头的格式: /** **************************************************************************** * @file x ...

  5. MIT 6.828 Lab04 : Preemptive Multitasking

    目录 Part A:Multiprocessor Support and Cooperative Multitasking Multiprocessor Support 虚拟内存图 Exercise ...

  6. 用C++基础语句写一个五子棋游戏

    (这是一个颜色会变化的呦) #include <iostream> using namespace std; int b[][]; int n; int m; void qipan() { ...

  7. 多线程的指令重排问题:as-if-serial语义,happens-before语义;volatile关键字,volatile和synchronized的区别

    一.指令重排问题 你写的代码有可能,根本没有按照你期望的顺序执行,因为编译器和 CPU 会尝试指令重排来让代码运行更高效,这就是指令重排. 1.1 虚拟机层面 我们都知道CPU执行指令的时候,访问内存 ...

  8. Kubernetes 编写自定义 controller

    原文链接:Kubernetes编写自定义controller 来自kubernetes官方github的一张图: 如图所示,图中的组件分为client-go和custom controller两部分: ...

  9. 微信支付宝个人免签约即时到帐接口开发附demo

    支付界面: 扫码支付界面: 付成功后可以跳转到你程序指定的地址! demo: http://likeyunba.com/WeChat-Pay/

  10. wordpress个人常用标签调用

    wordpress常见标签调用,老是容易忘记,又要找半天,干脆搬到网站上. <?php bloginfo('name');?>网站名称 url <?php echo home_url ...