2

关于异步 CTP 的一些相关问题:

  • 我可以使用枚举器方法和枚举器方法迭代迭代器块(IEnumerable<T>返回收益T)。方法的类比是什么?非调用方法如何接收和处理任何ed 项目然后?你能提供一个简短的例子吗?我只是没看到。GetEnumerator()MoveNext()Current()asyncasyncawaitContinueWith()

  • 此外,在以下示例async方法中,MyAwaitable有一个GetAwaiter()方法。如果GetAwaiter()返回 a stringbut THuhis not string,编译器不会抱怨。THuh和之间存在什么类型的约束/期望GetAwaiter()

    async Task<THuh> DoSomething()
    {
         var x = await new MyAwaitable("Foo");
    
         var y = await new MyAwaitable("Bar");
    
         return null;
    } 
    
  • 请解释 C# 规范草案的以下行。async Task<T>方法应该永远不会被使用return吗?default(T)我看到一些似乎不遵循此规则的示例 - 返回值似乎可以达到,并且该值是非默认值。这个值是不可访问的吗?如果是这样,为什么尴尬的无法访问的 return 语句?

在返回类型Task<T>为 some的异步函数中T,return 语句必须具有可隐式转换为 的表达式T,并且主体的端点必须不可到达。

  • 规范说“GetAwaiter、IsCompleted、OnCompleted 和 GetResult 的所有内容都是“非阻塞”的”——那么应该以什么方法定义(可能)长时间运行的操作?

谢谢!

4

2 回答 2

7

我正在尝试使用 Iterator Blocks 转换我所做的(尴尬但成功)的延续传递,并将其转换为使用异步。我想我正在以新的方式战斗。了解这种变化感觉就像撕开 3 英寸宽的 Velcro 条。

我能理解你会有这种感觉。我不鼓励人们尝试从迭代器块中构建 CPS,因为无论迭代器和 CPS 有什么共同的底层机制,它确实不是一个很好的选择。迭代器块设计用于快速制作将数据结构转换为序列或将序列转换为不同序列的方法;它们并非旨在解决呼叫与当前继续的一般问题。

就此而言, async/await 也不是精确地调用当前继续,尽管它显然更接近一个数量级。Async/await 旨在使基于任务的异步更容易;它通过将代码重写为延续传递样式的形式来实现这一点,这是一个实现细节。

我在相关主题上写的这个答案可能会有所帮助:

c# 5.0 中新的异步特性如何用 call/cc 来实现?

我怀疑您遇到的概念问题是在迭代器样式的异步中,“协调器”(确定迭代器块何时从中断处恢复的东西)是您的代码。您编写一些代码并决定何时调用 MoveNext 来抽取迭代器。使用基于任务的异步,其他一些代码可以为您做到这一点。当一个任务完成时,它很有可能将该事实发布到某个消息队列中,然后当消息队列被抽出时,结果会激活延续。您的代码中没有明确的“MoveNext”可供您指出;相反,任务已经完成并且知道它自己的延续这一事实足以确保将延续放入工作队列以供最终执行。

如果您有更多问题,我鼓励您将它们发布在 SO 和/或异步论坛上。

于 2011-12-09T01:00:42.730 回答
1

在您的DoSomething示例中,编译器没有抱怨,因为您的 MyAwaitableGetResult方法的类型与THuh. 相关的语句THuhreturn null;。null 文字可以隐式转换为THuh,所以一切都很好。

IEnumerable类似于的关键字awaitforeachawait需要适合特定模式的类型,foreach. 一种是消费可等待类型的机制,另一种是消费可枚举类型的机制。

另一方面,迭代器块 (yield returnyield break) 是用于定义可枚举类型的机制(通过编写方法而不是显式声明类型)。这里的比喻是async关键字。

async为了详细说明and之间的类比yield return,请注意返回的迭代器块IEnumerable<int>可以包含yield return 42;类似的语句,返回的异步方法Task<int>可以包含语句yield return 42;。请注意,在这两种情况下,返回表达式的类型不是方法的返回类型,而是方法返回类型的类型参数


如果你还没有这样做,你真的应该阅读 Eric Lippert 关于这些主题的博客:

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

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

此外,如果这个概念对您来说是新的(就像对我一样),关于 Async 系列以外的关于 continuation-passing 风格的帖子可能会很有用:

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


最后,有关示例,请参阅Eric 的博客文章链接到他的 MSDN 文章和同一问题中的相关文章以及 Bill Wagner 在http://msdn.microsoft.com/en-us/vstudio/hh533273上的后续文章

编辑

我看到一些似乎不遵循此规则的示例 - 返回值似乎可以达到,并且该值是非默认值。这个值是不可访问的吗?如果是这样,为什么尴尬的无法访问的 return 语句?

短语“body 的端点必须不可到达”意味着你必须有一个 return 语句。主体的端点位于 return 语句之后,并且通过 return 语句变得不可访问。使用正常的 int 返回方法的示例:

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.
}

编辑 2

这是一个计算字符串后半部分的等待器的简短示例。该示例传递了一个将结果打印到控制台的延续。这不是线程安全的!

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();
    }
}
于 2011-12-08T18:20:15.567 回答