C#上位机序列2: 同步异步(async、await)
模拟下载
namespace AsyncWaitDemo
{
public class DownloadUtil
{
/// <summary>
/// 正常使用async/await时,符合正常的业务逻辑:
/// 1. 通知用户下载开始
/// 2. 异步下载
/// 3. 等待异步下载完成后给用户提示下载完成
/// </summary>
public static async void DownloadHandle()
{
Logger.Info("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
await Download();
Logger.Info("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
/// 下载
/// </summary>
/// <returns></returns>
public static Task Download()
{
return Task.Run(() =>
{
Logger.Info("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
Logger.Info("10%"); Task.Delay(1000).Wait();
Logger.Info("30%"); Task.Delay(1000).Wait();
Logger.Info("50%"); Task.Delay(1000).Wait();
Logger.Info("60%"); Task.Delay(1000).Wait();
Logger.Info("80%"); Task.Delay(1000).Wait();
Logger.Info("99%"); Task.Delay(1000).Wait();
Logger.Info("100%");
});
}
}
}
同步异步
namespace AsyncWaitDemo
{
internal class Program
{
static void Main(string[] args)
{
//DownloadUtil.DownloadHandle();
//WorkUtil.WorkerASync("异步");// 异步
//WorkUtil.WorkerASync("同步").Wait();// 同步
//LockUtil.MultiWrite();// 多次异步写入同一个txt文件
Logger.Info("End");
Console.ReadKey();
}
}
}
namespace AsyncWaitDemo
{
public class WorkUtil
{
static async Task Worker(string name)
{
await Task.Delay(1000);
Logger.Info(name);
}
public static async Task WorkerASync(string note)
{
// 方法内是同步
await Worker($"{note}等待::A");
await Worker($"{note}等待::B");
}
}
}
异步锁
using Nito.AsyncEx;
using System.Text;
namespace AsyncWaitDemo
{
public class LockUtil
{
static StreamWriter _writer = new StreamWriter("test.txt", true, Encoding.Default) { AutoFlush = true };
//static Mutex mutex = new Mutex();// 互斥锁
static AsyncLock _mutex = new AsyncLock();// nuget:Nito.AsyncEx
public static async void WriteAsync(string msg)
{
// 不加锁提示:The stream is currently in use by a previous operation on the stream
//await _writer.WriteLineAsync(msg);
// 加上互斥锁(不能解决问题)
//mutex.WaitOne();
//await _writer.WriteLineAsync(msg);
//mutex.ReleaseMutex();
// 加上异步锁(解决问题)
using (await _mutex.LockAsync())
{
Logger.Info("异步写入线程ID:->" + Thread.CurrentThread.ManagedThreadId);
await _writer.WriteLineAsync(msg);
}
}
public static void MultiWrite()
{
for (int i = 0; i < 20; i++)
WriteAsync(DateTime.Now.Ticks.ToString());
}
}
}
task和thread很大的一个区别就是,在task中如果有一个阻塞的话,整个task就会被阻塞住,当前的线程ID不会改变,在thread中如果有一个阻塞的话,会去执行另外的thread,然后回来执行原来的那个thread,线程ID会改变为其他的ID,增加CPU上下文切换
配合使用
Task.Run和await Task.Delay(100);
Task.Run(() =>
{
var foo = 2;
});
Task.Factory.StartNew(() =>
{
Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId);
}, TaskCreationOptions.LongRunning);// 设置线程是长时间运行,这时线程池就不会等待这个线程回收
在需要设置线程是长时间运行的才需要使用 Task.Factory.StartNew 不然就使用 Task.Run
using System.Diagnostics; namespace ThreadTask { internal class Program { static int sleep = 2000; static void Main(string[] args) { Console.WriteLine("Thread和Task sleep对比"); StartThread("1"); StartThread("2"); StartThread("3"); StartTaskA("A"); StartTaskB("B"); StartTaskC("C"); Console.ReadLine(); } static void StartThread(string note) { Stopwatch watch = new Stopwatch(); watch.Start(); Thread thd = new Thread(() => { int id1 = Thread.CurrentThread.ManagedThreadId; Thread.Sleep(sleep); int id2 = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"线程{note} 结束,消耗时间:{watch.ElapsedMilliseconds},切换:{id1 == id2}"); }); thd.Start(); } static void StartTaskA(string note) { Stopwatch watch = new Stopwatch(); watch.Start(); var task = Task.Factory.StartNew(async () => { int id1 = Thread.CurrentThread.ManagedThreadId; await Task.Delay(sleep); int id2 = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"任务{note} 结束,消耗时间:{watch.ElapsedMilliseconds},切换:{id1 == id2}"); }); } static void StartTaskB(string note) { Stopwatch watch = new Stopwatch(); watch.Start(); var Task2 = Task.Factory.StartNew(() => { int id1 = Thread.CurrentThread.ManagedThreadId; Task.Delay(sleep).Wait(); int id2 = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"任务{note} 结束,消耗时间:{watch.ElapsedMilliseconds},切换:{id1 == id2}"); }); } static void StartTaskC(string note) { Stopwatch watch = new Stopwatch(); watch.Start(); var task = Task.Run(async () => { int id1 = Thread.CurrentThread.ManagedThreadId; await Task.Delay(sleep); int id2 = Thread.CurrentThread.ManagedThreadId; Console.WriteLine($"任务{note} 结束,消耗时间:{watch.ElapsedMilliseconds},切换:{id1 == id2}"); }); } } }
区别:
①.Thread.Sleep()是同步延迟,既然是同步的,自然会阻塞当前线程;Task.Delay()是异步延迟,则不会阻塞线程;
②.Thread.Sleep()不能中途取消,Task.Delay()可以,delay有四个重载方法,需要取消的话,可以调用Delay(int millisecondsDelay, CancellationToken cancellationToken)这个方法;
③.在异步代码中通常使用await关键字调用Task.Delay(),而不是Thread.Sleep();
webapi
[HttpGet] public async Task<string> async_hello(string deviceno, int time1, int time2) { return await Task.Run(() => { return ResponseUtil.Info(true, "hello"); }); }
[HttpGet] public async Task<string> async_hello(string deviceno, int time1, int time2) { return await Task.Run(() => { var key_shop = RedisKey.ExistShop(deviceno); var shop = RedisHelper.Get<Shop>(key_shop); if (null == shop) { shop = FreeSqlHelper.GetShop(deviceno); if (null != shop) RedisHelper.Set(key_shop, shop); } // 计算费用 decimal moneyTotal = 0; string timeTotal = string.Empty; PayMoney.GetMoney(time2, time1, shop, ref moneyTotal, ref timeTotal); return ResponseUtil.Info(true, timeTotal + " " + moneyTotal); }); }
异步编程(async/await)主要用于处理I/O操作,如文件读写、网络请求、数据库查询等
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律