提问者:小点点

返回任务的接口的同步实现


类似于在同步代码中实现需要任务返回类型的接口,不过我很好奇是否应该忽略我的情况生成的编译器错误。

假设我有一个这样的接口:

public interface IAmAwesome {
    Task MakeAwesomeAsync();
}

在某些实现中,使用异步完成具有很好的好处。这正是接口试图允许的。

在其他情况下,也许很少,只需要一个简单的同步方法就可以使Awesome。我们假设实现如下所示:

public class SimplyAwesome : IAmAwesome {
    public async Task MakeAwesomeAsync() {
        // Some really awesome stuff here...
    }
}

null

此方法缺少'await'运算符,将同步运行。考虑使用运算符等待非阻塞API调用,或“await taskex.run(...)'”在后台线程上执行与CPU绑定的工作。

编译器实际上是在建议这个解决方案:

public class SimplyAwesome : IAmAwesome {
    public async Task MakeAwesomeAsync() {
        await Task.Run(() => {
            // Some really awesome stuff here, but on a BACKGROUND THREAD! WOW!
        });
    }
}

我的问题是-什么应该决定当我选择忽略这个编译器警告?在某些情况下,工作是如此的简单,以至于为它产生一个线程是不可否认的适得其反。


共3个答案

匿名用户

如果您确实希望同步地执行工作,您知道方法将始终同步地运行,这在这种情况下是可取的,那么无论如何,请忽略警告。如果你明白警告所告诉你的,并且感觉到它所描述的动作是正确的,那么这就不是问题。这是一个警告而不是错误的原因。

当然,另一种选择是不使用方法,而是简单地使用返回一个已经完成的任务。它将改变错误处理语义(除非您还捕获所有异常并将它们包装到返回的任务中),所以至少要注意这一点。如果您确实希望通过产生的传播异常,那么可能值得保留方法,只取消警告。

匿名用户

当我选择忽略此编译器警告时,应如何确定?在某些情况下,工作是如此的简单,以至于为它产生一个线程是不可否认的适得其反。

编译器没有说“在此方法中使用Task.Run”。它只是告诉您,您为他准备了一个方法,在方法声明中添加了修饰符,但实际上您并没有等待任何事情。

您可以采取三种方法:

A.您可以忽略编译器警告,一切仍将执行。请注意,生成状态机会有轻微的开销,但是您的方法调用将同步执行。如果操作很耗时,并且可能导致方法执行发出阻塞调用,这可能会使使用此方法的用户感到困惑。

b.将“AWESOME”的生成分离为同步接口和异步接口:

public interface MakeAwesomeAsync
{
    Task MakeAwesomeAsync();
}

public interface MakeAwesome
{
    void MakeAwesome();
}

C.如果操作不太耗时,您可以简单地使用将其包装在一个中。在选择这个之前,我肯定会测量运行CPU绑定操作所需的时间。

匿名用户

正如其他人已经指出的,你有三种不同的选择,所以这是一个见仁见智的问题:

  1. 保留它并忽略警告
  2. 重载 并返回完成的任务。/li>

我建议返回一个已经完成的任务:

public class SimplyAwesome : IAmAwesome 
{
    public Task MakeAwesomeAsync() 
    {
        // run synchronously
        return TaskExtensions.CompletedTask;
    }
}

原因有几个:

  • 创建一个方法有一点开销,即创建一个状态机并禁用一些优化(如内联),因为编译器添加了try-catch块。/li>
  • 您需要处理警告,以及其他团队成员在查看此代码时不知道在哪里。/li> 时有点不协调。这就像在代码中添加一样,它将执行相同的操作,但它没有传达方法的含义。/li>

Servy指出,返回任务会改变异常处理的语义。虽然这是真的,但我认为这不是问题。

首先,大多数代码调用一个方法并在相同的位置等待返回的任务(即),这意味着无论方法是否为,异常都将在相同的位置抛出。

其次,即使。NET Framework的任务返回方法也会同步抛出异常。例如,直接抛出异常,而不将其存储在返回的任务中,因此不需要任务来引发异常:

try
{
    Task.Delay(-2);
}
catch (Exception e)
{
    Console.WriteLine(e);
}

由于。NET开发人员需要在。NET中同步遇到异常,因此合理的做法是,他们也应该从您的代码中排除异常。