[C#] Parallel Programming in Practice: Basics of Task-Based Asynchronous Programming (Part 1)

        Chapter 8 introduces the practices and solutions available for asynchronous programming in C#, and also discusses when it is appropriate to use asynchronous programming. This chapter mainly introduces the async and await keywords.

        In fact, in previous studies, everyone has already understood these two keywords and used them a lot. In fact, I think there is no need to go into details, but let’s take a brief look here.

        Learn Engineering with this Tutorial: Magician Dix/HandsOnParallelProgramming · GitCode

        Due to space limitations, this article is the first one. The main content is the async and await keywords and manual implementation of TAP.


1. About the async and await keywords

        Using the async and await keywords, the code can maintain the state of the synchronous implementation with only minor changes.

1.1. Event-based asynchronous mode

        The most original way of writing is Event-Based Asynchronous Pattern (EAP), which requires registering callbacks to implement asynchronous operations. The code example is as follows:

        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);
        }

        The result of running this piece of code is as follows:

         It can be seen that asynchronous operations are indeed implemented, but because of the event-based writing method, we split the code into two parts. In some cases, this way of writing is convenient and logical. But when our asynchronous operation is of a process nature, this way of writing is more cumbersome: we need to connect methods in series to realize the process.

1.2. Task-based asynchronous mode

        We transform the above code into a task-based asynchronous mode:

        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);
        }

        Using the async and await keywords, the EAP mode is obvious. However, I don’t think there is an absolute advantage or disadvantage between the two. It all depends on the usage scenarios of each.

        The execution results are as follows:

         It can be seen that the main thread is still not blocked and asynchronous is also achieved. When our method contains the async keyword, it gives instructions to the compiler that the method will execute asynchronously (await) when necessary.

1.3. Type returned by asynchronous method

        Asynchronous methods have three return types: void, Task, and Task<T>. All asynchronous methods must return Task to wait (using the await keyword). They will not return immediately when called, but wait for asynchronous execution. The void method indicates that you do not want to wait, and calling the method will not block.

        Write an example here:

        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 !");
        }

        There is a difference in the return value between these two codes, but the difference when using it is that when returning to Task, you can choose whether to wait for execution to complete (use await), but when returning to void, you cannot wait.

2. Manually implement TAP

        In fact, Chapter 2: Task Parallelism has already discussed how to use the Task class to implement task-based asynchronous mode (Task-Based Asynchronous Parrern, TAP). When using the async keyword, the compiler will perform the required optimizations, which is very convenient. Here we introduce how to implement TAP manually.

TaskCompletionSource represents the producer side of a Task<TResult> that is not bound to the delegate and provides access to the consumer side through the Task property. https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=netstandard-2.1Here         we will use the TaskCompletionSource<T> class:

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

        From the writing point of view, it is better to use Task directly, and there are no other advantages.


        (To be continued)

         Learn Engineering with this Tutorial: Magician Dix/HandsOnParallelProgramming · GitCode

Guess you like

Origin blog.csdn.net/cyf649669121/article/details/131838459