【C#】并行编程实战:基于任务的异步编程基础(上)

        第八章介绍了 C# 中可用异步编程的实践和解决方案,还讨论了何时适合使用异步编程等。本章主要介绍 async 和 await 关键字。

        其实在之前的学习中,大家都已经了解过这两个关键字了,用得非常多。其实我觉得没有必要再赘述了,不过这里还是简单地看一看吧。

        本教程学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode

        因为篇幅限制,本篇为上篇,主要内容为 async 和 await 关键字 和手动实现 TAP。


1、关于 async 和 await 关键字

        使用 async 和 await 关键字,代码可以保持同步实现中的状态,仅需要做出很小的改变即可。

1.1、基于事件的异步模式

        最原始的写法是基于事件的异步模式(Event-Based Asynchronous Pattern , EAP),需要通过注册回调来实现异步操作。代码示例如下:

        public static void WebClientSample()
        {
            Debug.Log("WebClientSample ,Start !");
            WebClient client = new WebClient();
            client.DownloadStringCompleted += OnWebClientDownloaded;
            client.DownloadStringAsync(new Uri("https://blog.csdn.net/cyf649669121"));
            Debug.Log("WebClientSample ,开始下载!");
        }

        private static void OnWebClientDownloaded(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Debug.LogError($"下载失败 : {e.Error.Message}");
                return;
            }

            Debug.Log("下载成功");
            Debug.Log(e.Result);
        }

        这一段代码运行结果如下:

         可以看到确实是实现了异步操作,但是是因为基于事件的写法,我们将代码拆成了两部分。在某些情况下,这种写法很方便,也符合逻辑。但是当我们的异步操作是流程性质的,这样的写法就比较繁琐:我们需要将方法进行串联以实现流程。

1.2、基于任务的异步模式

        上述代码,我们将其改造成基于任务的异步模式:

        private void RunWithWebClientInTask()
        {
            WebClientInTask();
            Debug.Log("RunWithWebClientInTask End !");
        }

        public static async Task WebClientInTask()
        {
            Debug.Log("开始异步任务下载!");
            WebClient client = new WebClient();
            var result = await client.DownloadStringTaskAsync(new Uri("https://blog.csdn.net/cyf649669121"));
            Debug.Log("下载成功");
            Debug.Log(result);
        }

        使用 async 和 await 关键字的写法,和 EAP 模式显而易见。不过我倒不觉得这两者存在绝对的优劣,完全看各自的使用场景自行选择。

        执行结果如下:

         可见仍然没有阻塞主线程,同样实现了异步。当我们的方法包含 async 关键字时,就是给编译器指令,表示该方法将在必要的时候(await)异步执行。

1.3、异步方法返回的类型

        异步方法有3种返回类型:void、Task、Task<T>。所有异步方法必须返回 Task 才能等待(使用 await 关键字),调用时不会立即返回,而是等待异步执行。void 方法则表示不想等待的情况,调用方法不会阻塞。

        这里写个示例:

        public static async void WaitWithoutTask()
        {
            Debug.Log("WaitWithoutTask Start !");
            await Task.Delay(1000);
            Debug.Log("WaitWithoutTask End !");
        }

        public static async Task WaitWithTask()
        {
            Debug.Log("WaitWithTask Start !");
            await Task.Delay(1000);
            Debug.Log("WaitWithTask End !");
        }

        这两个代码就返回值有区别,但是使用时区别就是返回 Task 时可以选择是否等待执行完成(使用 await),而返回 void 时不能等待。

2、手动实现 TAP

        其实在 第二章:任务并行性 中已经讨论了如何使用 Task 类实现基于任务的异步模式(Task-Based Asynchronous Parrern,TAP)。使用 async 关键字时,编译器将执行所需优化,非常方便。这里我们介绍下如果手动实现 TAP。

TaskCompletionSource表示未绑定到委托的 Task<TResult> 的制造者方,并通过 Task 属性提供对使用者方的访问。https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=netstandard-2.1        这里我们会用到 TaskCompletionSource<T> 这个类:

        public static Task<int> MyTaskResult()
        {
            TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
            Thread.Sleep(1000);//会阻塞调用线程
            taskCompletionSource.SetResult(555);
            return taskCompletionSource.Task;
        }

        就从写法来看,不如直接用 Task,而且也没有什么别的优势。


        (未完待续)

         本教程学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode

猜你喜欢

转载自blog.csdn.net/cyf649669121/article/details/131838459