asynchronous - motor - Suprime la advertencia CS1998: este método asíncrono carece de ''esperar''
asynchronous vs synchronous (12)
Tengo una interfaz con algunas funciones de sincronización.
Metodología para volver a la Task
, creo. async
es un detalle de implementación, por lo que no se puede aplicar a los métodos de interfaz.
Algunas de las clases que implementan la interfaz no tienen nada que esperar, y algunas pueden arrojar.
En estos casos, puede aprovechar el hecho de que async
es un detalle de implementación.
Si no tiene nada que await
, puede simplemente devolver Task.FromResult
:
public Task<int> Success() // note: no "async"
{
... // non-awaiting code
int result = ...;
return Task.FromResult(result);
}
En el caso de arrojar NotImplementedException
, el procedimiento es un poco más prolijo:
public Task<int> Fail() // note: no "async"
{
var tcs = new TaskCompletionSource<int>();
tcs.SetException(new NotImplementedException());
return tcs.Task;
}
Si tiene muchos métodos lanzando NotImplementedException
(que a su vez puede indicar que algún tipo de refactorización a nivel de diseño sería bueno), entonces podría resumir la palabrería en una clase de ayuda:
public static class TaskConstants<TResult>
{
static TaskConstants()
{
var tcs = new TaskCompletionSource<TResult>();
tcs.SetException(new NotImplementedException());
NotImplemented = tcs.Task;
}
public static Task<TResult> NotImplemented { get; private set; }
}
public Task<int> Fail() // note: no "async"
{
return TaskConstants<int>.NotImplemented;
}
La clase auxiliar también reduce la basura que el GC tendría que recopilar, ya que cada método con el mismo tipo de devolución puede compartir sus objetos Task
y NotImplementedException
.
Tengo varios otros ejemplos de tipo "tarea constante" en mi biblioteca AsyncEx .
Tengo una interfaz con algunas funciones de sincronización. Algunas de las clases que implementan la interfaz no tienen nada que esperar, y algunas pueden arrojar. Es un poco molesto con todas las advertencias.
Cuando no se usa await en una función asíncrona.
¿Es posible suprimir el mensaje?
public async Task<object> test()
{
throw new NotImplementedException();
}
Advertencia CS1998: Este método asíncrono carece de operadores ''en espera'' y se ejecutará de forma síncrona. Considere usar el operador ''aguardar'' para esperar las llamadas API sin bloqueo, o ''esperar a que Task.Run (...)'' realice un trabajo de CPU en un hilo de fondo.
Aquí hay algunas alternativas dependiendo de la firma de su método.
public async Task Test1()
{
await Task.CompletedTask;
}
public async Task<object> Test2()
{
return await Task.FromResult<object>(null);
}
public async Task<object> Test3()
{
return await Task.FromException<object>(new NotImplementedException());
}
En caso de que ya establezca un enlace contra Reactive Extension, también puede hacer:
public async Task<object> NotImplemented()
{
await Observable.Throw(new NotImplementedException(), null as object).ToTask();
}
public async Task<object> SimpleResult()
{
await Observable.Return(myvalue).ToTask();
}
Reactive y async / await son sorprendentes por sí mismos, pero también juegan bien juntos.
Incluye necesarios son:
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
Existe una diferencia en las diferentes soluciones que puede encontrar en las respuestas y, estrictamente hablando, debe saber cómo llamará el método asincrónico, pero con un patrón de uso predeterminado que asume ".Wait ()" en el resultado del método - " return Task.CompletedTask" "es la mejor solución.
BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233537 Hz, Resolution=309.2589 ns, Timer=TSC
.NET Core SDK=2.1.2
[Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Clr : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2600.0
Core : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
Method | Job | Runtime | Mean | Error | StdDev | Median | Min | Max | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
--------------- |----- |-------- |-------------:|------------:|------------:|-------------:|-------------:|-------------:|-----:|-------:|-------:|-------:|----------:|
CompletedAwait | Clr | Clr | 95.253 ns | 0.7491 ns | 0.6641 ns | 95.100 ns | 94.461 ns | 96.557 ns | 7 | 0.0075 | - | - | 24 B |
Completed | Clr | Clr | 12.036 ns | 0.0659 ns | 0.0617 ns | 12.026 ns | 11.931 ns | 12.154 ns | 2 | 0.0076 | - | - | 24 B |
Pragma | Clr | Clr | 87.868 ns | 0.3923 ns | 0.3670 ns | 87.789 ns | 87.336 ns | 88.683 ns | 6 | 0.0075 | - | - | 24 B |
FromResult | Clr | Clr | 107.009 ns | 0.6671 ns | 0.6240 ns | 107.009 ns | 106.204 ns | 108.247 ns | 8 | 0.0584 | - | - | 184 B |
Yield | Clr | Clr | 1,766.843 ns | 26.5216 ns | 24.8083 ns | 1,770.383 ns | 1,705.386 ns | 1,800.653 ns | 9 | 0.0877 | 0.0038 | 0.0019 | 320 B |
CompletedAwait | Core | Core | 37.201 ns | 0.1961 ns | 0.1739 ns | 37.227 ns | 36.970 ns | 37.559 ns | 4 | 0.0076 | - | - | 24 B |
Completed | Core | Core | 9.017 ns | 0.0690 ns | 0.0577 ns | 9.010 ns | 8.925 ns | 9.128 ns | 1 | 0.0076 | - | - | 24 B |
Pragma | Core | Core | 34.118 ns | 0.4576 ns | 0.4281 ns | 34.259 ns | 33.437 ns | 34.792 ns | 3 | 0.0076 | - | - | 24 B |
FromResult | Core | Core | 46.953 ns | 1.2728 ns | 1.1905 ns | 46.467 ns | 45.674 ns | 49.868 ns | 5 | 0.0533 | - | - | 168 B |
Yield | Core | Core | 2,480.980 ns | 199.4416 ns | 575.4347 ns | 2,291.978 ns | 1,810.644 ns | 4,085.196 ns | 10 | 0.0916 | - | - | 296 B |
Nota: FromResult
no se puede comparar directamente.
Código de prueba:
[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkAsyncNotAwaitInterface
{
string context = "text context";
[Benchmark]
public int CompletedAwait()
{
var t = new CompletedAwaitTest();
var a = t.DoAsync(context);
a.Wait();
return t.Length;
}
[Benchmark]
public int Completed()
{
var t = new CompletedTest();
var a = t.DoAsync(context);
a.Wait();
return t.Length;
}
[Benchmark]
public int Pragma()
{
var t = new PragmaTest();
var a = t.DoAsync(context);
a.Wait();
return t.Length;
}
[Benchmark]
public int Yield()
{
var t = new YieldTest();
var a = t.DoAsync(context);
a.Wait();
return t.Length;
}
[Benchmark]
public int FromResult()
{
var t = new FromResultTest();
var t2 = t.DoAsync(context);
return t2.Result;
}
public interface ITestInterface
{
int Length { get; }
Task DoAsync(string context);
}
class CompletedAwaitTest : ITestInterface
{
public int Length { get; private set; }
public async Task DoAsync(string context)
{
Length = context.Length;
await Task.CompletedTask;
}
}
class CompletedTest : ITestInterface
{
public int Length { get; private set; }
public Task DoAsync(string context)
{
Length = context.Length;
return Task.CompletedTask;
}
}
class PragmaTest : ITestInterface
{
public int Length { get; private set; }
#pragma warning disable 1998
public async Task DoAsync(string context)
{
Length = context.Length;
return;
}
#pragma warning restore 1998
}
class YieldTest : ITestInterface
{
public int Length { get; private set; }
public async Task DoAsync(string context)
{
Length = context.Length;
await Task.Yield();
}
}
public interface ITestInterface2
{
Task<int> DoAsync(string context);
}
class FromResultTest : ITestInterface2
{
public async Task<int> DoAsync(string context)
{
var i = context.Length;
return await Task.FromResult(i);
}
}
}
Otra forma de preservar la palabra clave async (en caso de que quiera conservarla) es usar:
public async Task StartAsync()
{
await Task.Yield();
}
Una vez que complete el método, simplemente puede eliminar la declaración. Utilizo esto mucho, especialmente cuando un método puede esperar algo, pero no todas las implementaciones realmente lo hacen.
Otra opción, si desea mantener el cuerpo de la función simple y no escribir código para respaldarlo, es simplemente suprimir la advertencia con #pragma:
#pragma warning disable 1998
public async Task<object> Test()
{
throw new NotImplementedException();
}
#pragma warning restore 1998
Si esto es lo suficientemente común, podría colocar la declaración de inhabilitación en la parte superior del archivo y omitir la restauración.
http://msdn.microsoft.com/en-us/library/441722ys(v=vs.110).aspx
Puede soltar la palabra clave async del método y simplemente hacer que devuelva Task;
public async Task DoTask()
{
State = TaskStates.InProgress;
await RunTimer();
}
public Task RunTimer()
{
return new Task(new Action(() =>
{
using (var t = new time.Timer(RequiredTime.Milliseconds))
{
t.Elapsed += ((x, y) => State = TaskStates.Completed);
t.Start();
}
}));
}
Sé que este es un hilo viejo, y quizás esto no tendrá el efecto correcto para todos los usos, pero lo siguiente es lo más cerca que puedo llegar a poder simplemente lanzar una NotImplementedException cuando aún no he implementado un método, sin alterar la firma del método. Si es problemático, me encantaría saberlo, pero apenas me importa: solo lo uso mientras estoy en desarrollo, así que cómo funciona no es tan importante. Aún así, me gustaría saber por qué es una mala idea, si es así.
public async Task<object> test()
{
throw await new AwaitableNotImplementedException<object>();
}
Este es el tipo que agregué para hacer eso posible.
public class AwaitableNotImplementedException<TResult> : NotImplementedException
{
public AwaitableNotImplementedException() { }
public AwaitableNotImplementedException(string message) : base(message) { }
// This method makes the constructor awaitable.
public TaskAwaiter<AwaitableNotImplementedException<TResult>> GetAwaiter()
{
throw this;
}
}
Se puede producir cs1998 a continuación.
public async Task<object> Foo()
{
return object;
}
Entonces puedes reformar a continuación.
public async Task<object> Foo()
{
var result = await Task.Run(() =>
{
return object;
});
return result;
}
Si no tiene nada que esperar, regrese Task.FromResult
public Task<int> Success() // note: no "async"
{
... // Do not have await code
var result = ...;
return Task.FromResult(result);
}
Solo como una actualización de la Respuesta de Stephen, ya no es necesario que escriba la clase TaskConstants
ya que hay un nuevo método de ayuda:
return Task.FromException(new NotImplementedException());
// This is to get rid of warning CS1998, please remove when implementing this method.
await new Task(() => { }).ConfigureAwait(false);
throw new NotImplementedException();