多线程
多线程 using System.Threading
//创建了一个与主线程并行的子线程,lambda表达式中代码属于子线程
Thread thread = new Thread(() =>
{
_viewModel.SaveCustomer(name, idNumber, address);
});
//启动
thread.Start();
//让线程进入静默状态,单位ms
thread.Sleep(1000);
前台线程、后台线程与线程池托管
持续运行的子线程被称作前台线程,只有前台线程结束后程序才能被关闭 后台线程会随着主程序的结束而结束 .net中,除主线程外,任何线程都可以在任何时候切换为前台或后台线程
Thread thread = new Thread(() =>
{
});
//切换为后台线程
thread.IsBackgroud = true;
//启动
thread.Start();
托管在线程池中的线程全部是后台线程,通过new thread创建的线程全部是前台线程 线程池:通过线程池创建的线程生命周期托管在线程池内,线程池可以在不新建线程的情况下重复使用已经完成的线程,使线程数量维持在较低的水平
//通过线程池创建线程
//使用线程池创建线程需要参数o(代表object)
ThreadPool.QueueUserWorkItem((o) => {
Console.WriteLine($"线程id {Thread.CurrentThread.ManagedThreadId}");
});
结束线程与CancellationToken
不管程序有多少进程,进程间的资源相互共享,故C#封装了一个取消令牌CancellationToken
CancellationTokenSource tokenSource = new CancellationTokenSource();
//tokenSource是一个变量,需要将其传入子线程中
Thread thread = new Thread(() => { PrintHello(tokenSource.Token); });
thread.Start();
// 下载文件
Thread.Sleep(5000);
// 关闭子线程
//tokenSource.Cancel();
// 3s后关闭
tokenSource.CancelAfter(3000);
Console.WriteLine("退出主程序");
}
private static void PrintHello(CancellationToken token)
{
//IsCancellationRequested 取消token请求是否被提交
while (!token.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine("Hello from PrintHello");
}
}
不将前台线程切换为后台线程的情况下,实现主线程关闭并且子线程也关闭
Join 与 IsAlive
thread.Join();在主线程调用join方法后,主线程与子线程绑定,此时主线程被挂起,等待子线程执行结束后继续执行。
或者检测thread.IsAlive属性,判断子线程是否在工作
while(thread.IsAlive)
{
Console.WriteLine("子线程工作中");
Thread.Sleep(100);
}
资源竞争与线程锁lock
lock关键字,用于解决资源竞争的问题
static object lockedObj = new object();
static void AddText()
{
lock (lockedObj)
{
// 取得当前进程的id
File.AppendAllText(@"D:\test.txt", $"开始 {Thread.CurrentThread.ManagedThreadId} ");
Thread.Sleep(100);
File.AppendAllText(@"D:\test.txt", $"结束 {Thread.CurrentThread.ManagedThreadId} ");
}
}
异步 Task
同步,程序调用某方法,需要等待执行完成后才进行下一步操作,异步,程序调用某方法不做等待操作,在处理完成前就返回该方法继续执行接下来的操作
使用Task类创建异步处理并行逻辑(使用多线程处理并发)异步比多线程性能高
//当123没有依赖关系时
Task.Run(()=>{
calculate1();
})
Task.Run(()=>{
calculate2();
})
Task.Run(()=>{
calculate3();
})
//如果3需要12的执行结果
var task1 = Task.Run(()=>{
return calculate1();
})
var task2 = Task.Run(()=>{
return calculate2();
})
//等待12执行结束
Task.waitAll(task1,task2)
//获得异步等待对象
var awaiter1 = task1.GetAwaiter();
//获得执行结果
var result1 = awaiter1.GetResult();
var awaiter2 = task1.GetAwaiter();
var result2 = awaiter1.GetResult();
calculate3(result1,result2);
//在此基础上2又依赖1的执行结果
var task1 = Task.Run(() =>
{
return Calculate1();
});
//等待1执行完成
var awaiter1 = task1.GetAwaiter();
//当1执行完成后OnCompleted就会被调用
awaiter1.OnCompleted(() =>
{
var result1 = awaiter1.GetResult();
//在异步逻辑中继续创建异步逻辑
var task2 = Task.Run(() =>
{
return Calculate2(result1);
});
var awaiter2 = task2.GetAwaiter();
awaiter2.OnCompleted(() =>
{
var result2 = awaiter2.GetResult();
var result = Calculate3(result1, result2);
Console.WriteLine(result);
});
});
异步方法 async和await
需要使用async关键字修饰 返回类型
void Task Task<T> IAsyncEnumerable<T>(用于处理数据流)
Task Task<T>
类型会返回调用方然后开启新的进程继续其他工作,同时当前进程挂起,等待没有完成的工作 其中需要至少包含一个await表达式表示异步执行的任务 命名规范:异步方法要以Async结尾 async函数只能被async函数调用
static async void Calculate()
{
// Task -> 异步
// Thread -> 线程
//result1的数据类型由Calculate1Async的泛型决定
var result1 = await Calculate1Async();
var result2 = await Calculate2Async(result1);
var result = await Calculate3Async(result1, result2);
Console.WriteLine(result);
}
static async Task<int> Calculate1Async()
{
var result = 3;
Console.WriteLine($"Calculate1: {result}");
//task中的等待
await Task.Delay(3000);
return result;
}
static async Task<int> Calculate2Async(int a)
{
var result = a*2;
Console.WriteLine($"Calculate2: {result}");
await Task.Delay(2000);
return result;
}
static async Task<int> Calculate3Async(int a, int b)
{
var result = a + b;
Console.WriteLine($"Calculate3: {result}");
await Task.Delay(1000);
return result;
}
异步,多线程,并发,并行
使用
Task.Factory.StartNew()
创建新的异步 异步不会在后台创建新的线程,异步是回调而不是多线程执行 异步在执行结束后会创建一个线程通知主程序,这个过程就叫做回调并发 两只地鼠独立完成一件事情,装卸书的过程中不会排队 并行 使用多个节点 服务器集群部署相同的服务