C # 8.0 new features: Async Streams

Reference link:

https://www.infoq.com/articles/Async-Streams/

Chinese blog in a very straightforward one:

https://www.cnblogs.com/CoderAyu/p/10680805.html

Producers and consumers pull mode and push mode

C # 8.0 adds a new feature: Async Streams.

Before about this new feature, we need to understand what is a "push and pull mechanism."

In the actual production environment or business logic, the most consistent with the mode of producers and consumers. Producers of consumer spending produced products, but will always be some differences between the two, either in short supply or oversupply.

When the producers to produce products quickly, while slower consumer spending products, we generally use the "pull mode", this model is also more common mode, as long as consumers go for products usually have, so consumers generally do not We have to wait.

But when consumers spend more quickly, but few producers slow production, and then continue to use the "pull mode", consumers may be waiting in line, in this way will cause consumers to wait too long.

Business model this phenomenon will cause consumers to not use your product, because consumers tend to be inert, inert products tend not to be patient, so based on this case, we generally use the "push mode", this model under that once produced goods producers, on the way by pushing pushed to consumers, consumers do not need to take the initiative to pull the product.

The picture below shows two models of consumer and producer uneven taken: pull and push

Inert pull based mechanism Async Streams

Async asynchronous nature for solving solutions to prevent the main thread is blocked when the program takes a long time to run operations

In the asynchronous nature of the C # 5.0 is added. Await use only one result acquisition. After 8.0 allows asynchronous return multiple values thus provide a representation of asynchronous data source manner. (Asynchronous data source may be understood as a data source continuously back through the asynchronous operation result to the main thread. Alternatively the reaction is asynchronous stream programming model used in Java and JavaScript.)

C # 5 introduced the Async / Await, to improve user interface responsiveness and the ability to access Web resources. In other words, the method for performing the asynchronous thread is not blocked and returns a scalar result of the asynchronous operation.

A significant lack of existing asynchronous method is that it must return a scalar result (a value). This method such as async Task <int> DoAnythingAsync (), the result is an integer DoAnythingAsync (a value).

Because of this limitation, you can not use this function with the yield keyword, and nor can it be used with async IEnumerable <int> (asynchronous return enumeration).

C # 8 Async Streams newly proposed scalar result the restriction is removed, and allows a plurality of asynchronous method returns the result.

Why offer this feature? This is said to be reactive programming.

Reactive Extensions (Rx) is another way to solve the problem of asynchronous programming. Rx becoming increasingly popular developers. Many other programming languages ​​(such as Java and JavaScript) have implemented this technology (RxJava, RxJS). Rx programming model push (Push Programming Model) based, also known as reactive programming. Reactive programming is one type of event-driven programming, which is the data processing notification instead.

To provide some cases leads to the theme

Case1

        /// <summary>
        /// simple foreach then return the result
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
       public  static int SumFromOneToCount(int count)
        {
            ConsoleExt.WriteLine("SumFromOneToCount called!");

            var sum = 0;
            for (var i = 0; i <= count; i++)
            {
                sum = sum + i;
            }
            return sum;
        }

        /// <summary>
        /// we use this method to get the data from producter, but if the producter's logic is complexity enough, 
        /// the main thread will be blocked for a long time, so that why we need the async to slove this problem.
        /// </summary>
        public void SumFromOneToCount() 
        {
            const int count = 5;
            ConsoleExt.WriteLine($"Starting the application with count: {count}!");
            ConsoleExt.WriteLine("Classic sum starting.");
            ConsoleExt.WriteLine($"Classic sum result: {Producter.SumFromOneToCount(count)}");
            ConsoleExt.WriteLine("Classic sum completed.");
            ConsoleExt.WriteLine("################################################");
            ConsoleExt.WriteLine(Environment.NewLine);
        }

Case2

        /// <summary>
        /// in this function, the result was splited by servral results and displayed in the consle.
        /// this is the benifit of yield feature. we can get some of the result before we get the whole result.
        /// but we can also see, the producter's logic still block the main thread.
        /// </summary>
        public void SumFromOneToCountYield() 
        {
            const int count = 5;
            ConsoleExt.WriteLine("Sum with yield starting.");
            foreach (var i in Producter.SumFromOneToCountYield(count))
            {
                ConsoleExt.WriteLine($"Yield sum: {i}");
            }
            ConsoleExt.WriteLine("Sum with yield completed.");

            ConsoleExt.WriteLine("################################################");
            ConsoleExt.WriteLine(Environment.NewLine);
        }

        /// <summary>
        /// simple foreach and yield return the result
        /// the caller can only get one result object is sum
        /// but will get the every iteration sum vaule because of yield statement
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
       public static IEnumerable<int> SumFromOneToCountYield(int count)
        {
            ConsoleExt.WriteLine("SumFromOneToCountYield called!");

            var sum = 0;
            for (var i = 0; i <= count; i++)
            {
                sum = sum + i;

                yield return sum;
            }
        }

Case3

        /// <summary>
        /// the async feature help us to slove the main thread block problem. 
        /// but we still got the whole result at one time.
        /// we need some solution to fix both main threads block and split the results.
        /// how to fix that requirement? 
        /// </summary>
        public async void SumFromOneToCountAsync() 
        {
            const int count = 5;
            ConsoleExt.WriteLine("async example starting.");
            var result = await Producter.SumFromOneToCountAsync(count);
            ConsoleExt.WriteLine("async Result: " + result);
            ConsoleExt.WriteLine("async completed.");

            ConsoleExt.WriteLine("################################################");
            ConsoleExt.WriteLine(Environment.NewLine);
        }

        /// <summary>
        /// async to get the sum value after a long foreach logic.
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public static async Task<int> SumFromOneToCountAsync(int count)
        {
            ConsoleExt.WriteLine("SumFromOneToCountAsync called!");

            var result = await Task.Run(() =>
            {
                var sum = 0;

                for (var i = 0; i <= count; i++)
                {
                    sum = sum + i;
                }
                return sum;
            });

            return result;
        }

Case4

        /// <summary>
        /// we used async method and will avoid to block the main thread, 
        /// also we can get the every iteration of the value, but it is come from one collection, and this collection will be get at one time.
        /// now we need a new mode: every time the producter create a product, this product should get back to the customer.
        /// </summary>
        public async void SumFromOneToCountTaskIEnumerable() 
        {
            const int count = 5;
            ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable started!");
            var scs = await Producter.SumFromOneToCountTaskIEnumerable(count);
            ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable done!");

            foreach (var sc in scs)
            {
                ConsoleExt.WriteLine($"AsyncIEnumerable Result: {sc}");
            }

            ConsoleExt.WriteLine("################################################");
            ConsoleExt.WriteLine(Environment.NewLine);
        }

         /// <summary>
        /// async method to get the every iteration of the sum value
        /// because all the iteration's sum value was added into a collection.
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public static async Task<IEnumerable<int>> SumFromOneToCountTaskIEnumerable(int count)
        {
            ConsoleExt.WriteLine("SumFromOneToCountAsyncIEnumerable called!");
            var collection = new Collection<int>();

            var result = await Task.Run(() =>
            {
                var sum = 0;

                for (var i = 0; i <= count; i++)
                {
                    sum = sum + i;
                    collection.Add(sum);
                }
                return collection;
            });

            return result;
        }

The above four cases please read the corresponding summary, and then we come back to the above-mentioned Cases, could correspond to the following several business needs:

  • Synchronization pull

The client sends a request to the server, the client must wait (the client is blocked) until the server responds, as shown in FIG.

  • Asynchronous data pull

客户端发出数据请求然后继续执行其他操作。一旦有数据到达,客户端就继续处理达到的数据。

  • 异步序列数据拉取

客户端发出数据块请求,然后继续执行其他操作。一旦数据块到达,客户端就处理接收到的数据块并询问下一个数据块,依此类推,直到达到最后一个数据块为止。这正是 Async Streams 想法的来源。下图显示了客户端可以在收到任何数据时执行其他操作或处理数据块。

显然我们无法实现最后一个需求, 异步序列数据拉取. 这也是为什么我们需要 Async Streams, 其核心是:

        public interface IAsyncEnumerable<out T>
        {
            IAsyncEnumerator<T> GetAsyncEnumerator();
        }
        public interface IAsyncEnumerator<out T> : IAsyncDisposable
        {
            Task<bool> MoveNextAsync();
            T Current { get; }
        }
        // Async Streams Feature 可以被异步销毁 
        public interface IAsyncDisposable
        {
            Task DiskposeAsync();
        }

此时生产者的代码如下:

        /// <summary>
       /// 1. make this method to be an async method.
       /// 2. create task.delay to intent a long work for get the sum result.
       /// 3. use yield return to return the temp sum value to customer.
       /// </summary>
       /// <param name="count"></param>
       /// <returns></returns>
       public async static IAsyncEnumerable<int> SumFromOneToCountAsyncYield(int count)
       {
           ConsoleExt.WriteLine("SumFromOneToCountYield called!");
           var sum = 0;
           for (var i = 0; i <= count; i++)
           {
               ConsoleExt.WriteLine($"thread id: {System.Threading.Thread.GetCurrentProcessorId()},  current time: {DateTime.Now}");
               sum = sum + i;
               await Task.Delay(TimeSpan.FromSeconds(2));
               yield return sum;
           }
       }

消费者的代码如下:

        public static async Task SumFromOneToCountAsyncYield() 
        {
            const int count = 5;
            ConsoleExt.WriteLine("Sum with yield starting.");
            await foreach  (var i in Producter.SumFromOneToCountAsyncYield(count))
            {
                  ConsoleExt.WriteLine($"Yield sum: {i}");
            }
            ConsoleExt.WriteLine("Sum with yield completed.");

            ConsoleExt.WriteLine("################################################");
            ConsoleExt.WriteLine(Environment.NewLine);
        }

所以 Async Streams 为我们解决了什么问题?

这个还是比较关注的, 因为我们得知道在什么样的场景下, 去使用这个技术, 去解决一个什么样的实际问题.

根据 Async Streams 的技术解析来看, 由于这项技术本质上属于一种惰性拉取模式. 那么就是适用于解决异步延迟加载的问题,例如从网站下载数据或从文件或数据库中读取并记录.

行文最后附上我实践过的code link : https://github.com/itdennis/DennisDemos/blob/master/DennisCoreDemos/Csharp%208/Async%20streams/Producter.cs

Guess you like

Origin www.cnblogs.com/it-dennis/p/11585998.html