yield-return...">

"Iteration" by asynchronous method

A few related questions about async CTP:

  • I can iterate over the iterator block ( IEnumerable<T> yield-return T ) using GetEnumerator() , and then the enumerator methods MoveNext() and Current() . What is async analog method? How can the invocation method not async receive and process any await ed elements and then ContinueWith() ? Can you give a brief example? I just don't see it.

  • In addition, in the following async example, the MyAwaitable method has a GetAwaiter() method. If GetAwaiter() returns string , but THuh not string , the compiler does not complain. What are the type constraints / expectations between THuh and GetAwaiter() ?

     async Task<THuh> DoSomething() { var x = await new MyAwaitable("Foo"); var y = await new MyAwaitable("Bar"); return null; } 
  • Please explain the following line of a C # draft. Are there async Task<T> return a default(T) methods that will never be used? I see several examples that do not seem to follow this rule - the return value seems reachable, and the value is not the default. Is this value unavailable? If so, why the inconvenient inaccessible return statement?

In an asynchronous function with the return type Task<T> for some T , the return statements must have an expression that is implicitly converted to T , and the endpoint of the body must be inaccessible.

  • The spectrum says: "All GetAwaiter, IsCompleted, OnCompleted, and GetResult are designed to be" non-blocking, "and then in which method should a potential long-term operation be defined?

Thanks!

+2
source share
2 answers

I am trying to convert a continuation-walkthrough that I did (inconveniently, but successfully) with Iterator Blocks and converted it instead of async. I think I'm fighting in a new way. Understanding the change is like tearing 3-inch wide Velcro strips.

I can understand how you can feel that way. I discourage people from trying to build CPS from iterator blocks, because this is actually not very convenient, regardless of what the basic mechanisms of iterators and CPS have in common. Iterator blocks are designed to quickly create methods that turn data structures into sequences or turn sequences into different sequences; they are not intended to solve the general problem of continuing a call over a period.

In this case, async / await is not exactly a challenge-with-continuation of the current, although it is approaching an order of magnitude, obviously. Async / await is designed to simplify task-based asynchrony; that he does this by rewriting code in the form of a continuation of the transfer style is an implementation detail.

This answer I wrote on the topic may help:

How can I implement the new async function in C # 5.0 using the / cc call?

I suspect that the conceptual problem you are facing is that in the asynchronous style of the iterator, the "orchestra" - what finds out when the iterator block returns, where it left off - is your code. You write code and you decide when to call MoveNext to pump the iterator. Using task-based asynchrony, some other code codes do this for you. When the task is completed, the probability is good that it places this fact in the message queue somewhere, and then, when the message queue is pumped up, the continuation is activated with the result. There is no explicit β€œMoveNext” in your code that you can point to; rather, the fact that the task is completed and knows its own continuation is sufficient to ensure that the continuation in the work queue continues for possible execution.

If you have additional questions, I recommend that you post them on the SO and / or asynchronous message forum.

+7
source

In your DoSomething example, the compiler does not complain, because the type of your MyAwaitable GetResult method has nothing to do with THuh . The THuh related THuh is return null; . A null literal is implicitly converted to THuh , so everything is fine.

The IEnumerable keyword, similar to await , is foreach . await requires a type that matches a specific pattern, and also foreach . One of them is a mechanism for consuming expected types, the other is for using enumerated types.

On the other hand, iterator blocks ( yield return and yield break ) are mechanisms for defining enumerated types (by writing a method, not explicitly declaring a type). The counterpart here is the async .

To clarify the analogy between async and yield return , note that the iterator block that returns IEnumerable<int> may contain a yield return 42; likewise, the async method that returns Task<int> may contain a yield return 42; >. Note that in both cases, the type of the return expression is not the type of the return method, but rather an argument of the type of the return type of the method.


If you have not already done so, you really should read Eric Lippert's blog on these topics:

http://blogs.msdn.com/b/ericlippert/archive/tags/Async/

http://blogs.msdn.com/b/ericlippert/archive/tags/Iterators/

In addition, continuation-style messages other than those in the Async series can be useful if the concept is new to you (as it was for me):

http://blogs.msdn.com/b/ericlippert/archive/tags/continuation+passing+style/


Finally, for examples, see Eric's blog entry related to his MSDN article and related articles in the same issue , and Bill Wagner's subsequent article at http://msdn.microsoft.com/en-us/vstudio/hh533273

EDIT

I see several examples that do not seem to follow this rule - the return value seems reachable, and the value is not the default. Is this value unavailable? If so, why the uncomfortable invalid expression of return?

The phrase "the endpoint of the body must be inaccessible" means that you must have a return statement. The endpoint of the body comes after the return statement and becomes unreachable with the return statement. An example using the regular int-return method:

 public int Main() { Console.WriteLine("X"); //after this comment is the reachable end point of the body; this method therefore won't compile. } public int Main() { Console.WriteLine("X"); return 0; //anything after the return statement is unreachable, including the end point of the body; this method therefore will compile. } 

EDIT 2

Here is a short, trivial awaiter example that computes the last half of a string. The example passes a continuation that prints the result to the console. This is not thread safe!

 public static class StringExtensions { public static SubstringAwaiter GetAwaiter(this string s) { return new SubstringAwaiter(s, s.Length / 2, s.Length - s.Length / 2); } } public class SubstringAwaiter { private readonly string _value; private readonly int _start; private readonly int _length; private string _result; private Action _continuation; public SubstringAwaiter(string value, int start, int length) { _value = value; _start = start; _length = length; } public bool IsCompleted { get; private set; } public void OnCompleted(Action callback) { if (callback == null) return; _continuation += callback; } public string GetResult() { if (!IsCompleted) throw new InvalidOperationException(); return _result; } public void Execute() { _result = _value.Substring(_start, _length); IsCompleted = true; if (_continuation != null) _continuation(); } } public class Program { public static void Main() { var awaiter = "HelloWorld".GetAwaiter(); awaiter.OnCompleted(() => Console.WriteLine(awaiter.GetResult())); awaiter.Execute(); } } 
+1
source

Source: https://habr.com/ru/post/903421/


All Articles