共计 6387 个字符,预计需要花费 16 分钟才能阅读完成。
.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>,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}"); | |
} |
参考
- 基于工作的异步模式 https://docs.microsoft.com/zh…
- 应用 Async 和 Await 的异步编程 https://docs.microsoft.com/zh…