C# 让两个需要循环顺序执行的函数进行异步交叉执行,提高执行速度

有的时候我们需要让2个函数按照顺序循环执行,比如将数据库的数据写到硬盘上,

我们很容易就想到让他们异步执行,避免阻塞,但是为了保证数据的顺序一致,我们又需要整个队列来存放数据,感觉比较麻烦,

今天研究了下,通过异步和信号量控制实现了两个函数异步交叉执行的效果

    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(temp); //处理当前包数据
            }
        }
    }

 

posted @ 2024-01-15 18:30  WmW  阅读(30)  评论(0编辑  收藏  举报