有的时候我们会有2个函数需要顺序执行,比如将数据库的数据写到硬盘上,
读数据库和写硬盘都是IO比较慢的操作,于是我们很容易就想到让他们异步执行,避免阻塞,提高性能,但是为了保证数据的顺序一致,我们又需要整个队列来存放数据,感觉比较麻烦,今天研究了下,通过异步和信号量控制实现了两个函数异步交叉执行的效果
internal class Tester { int num = 0; readonly int time_getData = 2000; readonly int time_processData = 1000; public async Task Test() { await Task.CompletedTask; Stopwatch sw = new Stopwatch(); sw.Start(); StartCrossLoopTask(GetData, ProcessData); sw.Stop(); var max = (new[] { time_getData, time_processData }).Max(); var r1 = time_getData + time_processData + max * (num - 1); //除了第一包,后面的都是异步交叉的 var r2 = (time_getData + time_processData) * num; //同步执行会阻塞 Console.WriteLine($"实际执行{sw.ElapsedMilliseconds},预估{r1},如果同步执行{r2}"); } string GetData() { if (num > 10) return null; //控制退出 Thread.Sleep(time_getData); //模拟读数据 string data = num.ToString(); Log.Info($"读取数据:{data}"); num++; return data; } void ProcessData(string data) { Thread.Sleep(time_processData); //模拟处理数据 Log.Info($" 处理数据:{data}"); } /// <summary> /// 开启一个交叉循环任务,这是一个循环任务,先getData获取数据,再processData处理数据,直到getData返回null, /// 该函数通过异步和信号量控制,实现了在processData的同时异步getData下一包数据,这样交叉执行,提高了性能 /// </summary> public static void StartCrossLoopTask<T>(Func<T> getData, Action<T> processData) where T : class { Semaphore semaphore1 = new Semaphore(1, 1); //获取数据的信号量 Semaphore semaphore2 = new Semaphore(0, 1); //处理数据的信号量 T data = null; //接收数据的引用 Task.Run(() => { //异步执行,实现非阻塞 while (true) { semaphore1.WaitOne(); //通过信号量控制顺序,保证最多提前获取一包数据,避免不可控的数据堆叠 data = getData(); semaphore2.Release(); //获取到数据后通知处理函数 if (data == null) return; //没有数据时就认为结束了 } }); T temp = null; //处理数据的引用 while (true) { semaphore2.WaitOne(); //阻塞,等待数据 if (data == null) return; //这里也得判断退出,否则会卡死 temp = data; //将数据指向临时引用,避免引用被覆盖 semaphore1.Release(); //释放信号量,处理的同时并行获取下一包数据,实现非阻塞 processData(data); //处理当前包数据 } } }
标签:异步,getData,processData,C#,num,time,执行,data From: https://www.cnblogs.com/luludongxu/p/17966015