C#异步迭代IAsyncEnumerable应用实现

最近用WPF做金税盘开发中有这样一个需求,批量开票每次开票都需要连接一次金税盘。

比如我有发票 a, b ,c ,d e 这五张发票,每次开具发票都需要调用金税盘底层,才能正常开票。

首先,尝试写第一个方法

private  void Button_Click(object sender, RoutedEventArgs e)
        {
            var dateStart = DateTime.Now;  //记录用时的起始时间
            DebugText = string.Empty;
            List fpList = new List() { "a", "b", "c", "d" };

            foreach (var item in MockIO)
            {
                var dateEnd = DateTime.Now;
                var timeSpan = dateEnd - dateStart;//记录开票用时
                DebugText += item + " " + timeSpan.TotalSeconds + "\r\n";

            }
        }

     /// 
        /// 批量开票方法
        /// 
        /// 
        /// 
        public static IEnumerable MockIO(List ls)
        {

            foreach (var item in ls)
            {
                Task.Delay(1000).Wait();
                yield return item;
                Debug.WriteLine(Thread.GetCurrentProcessorId());
            }
        }

来看效果

C#异步迭代IAsyncEnumerable应用实现_第1张图片

很明显,发生了UI阻塞情况。因为我们并未对代码做任何异步处理。接下来,我们开始尝试修改。

首先,我们尝试按照常规异步方法修改 MockIO 函数,增加 async 关键词,返回结果增加 Task, 内部对IO操作添加 await。

修改完毕后,编译并没有通过,VS对该方法报异常

C#异步迭代IAsyncEnumerable应用实现_第2张图片

通过提示信息,我们可以发现,返回值 Task> 并不是可以迭代的,因为我们采用了 yield 来返回值,所以我们需要一个可以迭代的返回值。

比如改成这样

C#异步迭代IAsyncEnumerable应用实现_第3张图片

但是,这样一次就返回一组 Task ,没有用到方便的 yield;

此时,就可以用到 IAsyncEnumerable 来设计了,IAsyncEnumerable是C# 8.0引入的新特性,在异步迭代中,非常方便。如上述代码,可以直接修改为

public static async IAsyncEnumerable MockIOAsync(List ls)
        {
            foreach (var item in ls)
            {
                Task> task = Task>.Factory.StartNew(async () =>
               {
                   await Task.Delay(1000);
                   return item;

               });

                yield return await task.Result;
            }
        }

我们再运行调试,看一下效果

C#异步迭代IAsyncEnumerable应用实现_第4张图片

我们可以看到,不仅UI没有被阻塞,同时,传回的值也是一个接一个的传过来的,符合我们的预期。

扩展:虽然上述步骤我们完成的UI的非阻塞的实现,但是我们整个开票用时并没有节省。

接下来,我将继续修改 MockIOAsync 方法,将实现迭代器内部的多线程操作。

修改后的代码如下

public static async Task> MockIOPerformanceAsync(List ls)
        {
            List lss = new List();
            List tasks = new List();
            foreach (var item in ls)
            {

                Task task = new Task(() =>
              {
                  Task.Delay(1000).Wait();
                  Debug.WriteLine(Thread.GetCurrentProcessorId());
                  lss.Add(item);
              });
                tasks.Add(task);
                task.Start();

            }
            foreach (var item in tasks)
            {
                await item;
            }
            return lss;
        }

效果展示:

C#异步迭代IAsyncEnumerable应用实现_第5张图片

嗯,速度很快,但是排序乱了,因为此方法在遍历中新建了线程,list 添加并不保证按照迭代器的顺序添加。有得有失。

到此这篇关于C#异步迭代IAsyncEnumerable应用实现的文章就介绍到这了,更多相关C#异步迭代IAsyncEnumerable内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的