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操作,如文件读写、网络请求、数据库查询等

posted @ 2023-09-06 12:00  CHHC  阅读(374)  评论(0编辑  收藏  举报