thread than operationcontextscope disposed different created being await async c# .net task-parallel-library code-contracts async-ctp

c# - than - Contratos de código y asincronía



async await wcf service (3)

Escribí esto pero olvidé pulsar "Publicar" ... :)

No hay soporte especializado para esto en este momento. Lo mejor que puedes hacer es algo como esto (no usar palabras clave async , pero la misma idea: es posible que el rewriter funcione de manera diferente bajo el CTP asíncrono, no lo he probado todavía):

public static Task<int> Do() { Contract.Ensures(Contract.Result<Task<int>>() != null); Contract.Ensures(Contract.Result<Task<int>>().Result > 0); return Task.Factory.StartNew(() => { Thread.Sleep(3000); return 2; }); } public static void Main(string[] args) { var x = Do(); Console.WriteLine("processing"); Console.WriteLine(x.Result); }

Sin embargo, esto significa que el método ''asíncrono'' no regresará hasta que la tarea haya terminado de evaluarse, por lo que el "procesamiento" no se imprimirá hasta que hayan transcurrido 3 segundos. Esto es similar al problema con los métodos que devuelven perezosamente IEnumerable s: el Contrato debe enumerar todos los elementos en IEnumerable para garantizar que la condición se IEnumerable , incluso si la persona que llama no usa realmente todos los elementos.

Puede solucionar esto cambiando el modo de contratos a Preconditions , pero esto significa que no se verificará ninguna condición posterior.

El comprobador estático tampoco puede conectar el Result con la lambda, por lo que recibirá un mensaje "Garantiza que no se haya probado". (En general, el verificador estático no prueba nada sobre lambdas / delegados de todos modos).

Creo que para obtener el soporte adecuado para Tareas / espera, el equipo de Contratos de Código tendrá que realizar Tareas de casos especiales para agregar la verificación de condición previa solo al acceder al campo Result .

¿Cuál es la forma recomendada de agregar condiciones posteriores a los métodos asíncronos que devuelven la Task<T> ?

He leído la siguiente sugerencia:

http://social.msdn.microsoft.com/Forums/hu-HU/async/thread/52fc521c-473e-4bb2-a666-6c97a4dd3a39

La publicación sugiere implementar cada método como síncrono, contraerlo y luego implementar una contraparte asíncrona como un contenedor simple. Desafortunadamente, no veo esto como una solución viable (quizás a través de mi propio malentendido):

  1. El método asíncrono, aunque se supone que es una envoltura para el método de sincronización, se deja sin ningún contrato de código real y, por lo tanto, puede hacer lo que desee.
  2. Es improbable que las bases de código comprometidas con la asincronía implementen contrapartes de sincronización para todo. Como resultado, la implementación de nuevos métodos que contienen la await en otros métodos asíncronos se ve forzada a ser asíncrona. Estos métodos son intrínsecamente asíncronos y no se pueden convertir fácilmente a síncronos. No son simplemente envoltorios.

Incluso si invalidamos el último punto diciendo que podríamos usar .Result o .Wait() lugar de await (lo que en realidad causaría que algunos SyncContext s se SyncContext , y tendríamos que volver a escribirlos en el método asíncrono de todos modos), Sigo convencido del primer punto.

¿Hay ideas alternativas, o hay algo que no haya visto sobre los contratos de código y TPL?


He señalado esto al equipo de Async, como han hecho otros. Actualmente, los Contratos y Async son (casi) mutuamente excluyentes. Entonces, al menos algunas personas en Microsoft son conscientes del problema, pero yo no sé qué planean hacer al respecto.

No recomiendo escribir métodos asíncronos como envoltorios para métodos de sincronización. De hecho, tendería a hacer lo contrario.

Las condiciones pueden funcionar. No lo he probado recientemente; Es posible que necesite un pequeño envoltorio alrededor de su método asíncrono que incluya las condiciones previas.

Las condiciones posteriores están bastante rotas.

Las aserciones y suposiciones funcionan normalmente, pero el verificador estático está realmente limitado porque las condiciones posteriores están rotas.

Las invariantes no tienen tanto sentido en el mundo de Async, donde el estado mutable tiende a interferir. (Async te empuja suavemente lejos de la POO y hacia un estilo funcional).

Con suerte, en VS vNext, los contratos se actualizarán con un tipo de condición posterior post-consciente, lo que también permitiría al comprobador estático funcionar mejor con aserciones en métodos asíncronos.

Mientras tanto, puede tener una post-condición ficticia escribiendo un supuesto:

// Synchronous version for comparison. public static string Reverse(string s) { Contract.Requires(s != null); Contract.Ensures(Contract.Result<string>() != null); return ...; } // First wrapper takes care of preconditions (synchronously). public static Task<string> ReverseAsync(string s) { Contract.Requires(s != null); return ReverseWithPostconditionAsync(s); } // Second wrapper takes care of postconditions (asynchronously). private static async Task<string> ReverseWithPostconditionAsync(string s) { var result = await ReverseImplAsync(s); // Check our "postcondition" Contract.Assume(result != null); return result; } private static async Task<string> ReverseImplAsync(string s) { return ...; }

Algunos usos de los contratos de código simplemente no son posibles, por ejemplo, la especificación de postcondiciones en miembros asíncronos de interfaces o clases base.

Personalmente, acabo de evitar los Contratos por completo en mi código de Async, con la esperanza de que Microsoft lo arregle en unos meses.


Publicación de una nueva respuesta a este hilo anterior como lo devuelve Google como la primera respuesta a la pregunta sobre CodeContract y Async

Actualmente, el Contrato sobre métodos asíncronos que devuelven la Tarea funciona correctamente y no hay necesidad de evitarlos.

Contrato estándar para el método asíncrono:

[ContractClass(typeof(ContractClassForIFoo))] public interface IFoo { Task<object> MethodAsync(); } [ContractClassFor(typeof(IFoo))] internal abstract class ContractClassForIFoo : IFoo { #region Implementation of IFoo public Task<object> MethodAsync() { Contract.Ensures(Contract.Result<Task<object>>() != null); Contract.Ensures(Contract.Result<Task<object>>().Status != TaskStatus.Created); Contract.Ensures(Contract.Result<object>() != null); throw new NotImplementedException(); } #endregion } public class Foo : IFoo { public async Task<object> MethodAsync() { var result = await Task.FromResult(new object()); return result; } }

Si crees que el contrato no parece correcto, estoy de acuerdo en que, al menos, parece engañoso, pero funciona. Y no parece que el redactor de contratos obligue a la evaluación de la tarea de manera prematura.

Cuando Stephen planteó algunas dudas, hizo más pruebas y los contratos en mi caso hicieron su trabajo correctamente.

Código utilizado para la prueba:

public static class ContractsAbbreviators { [ContractAbbreviator] public static void EnsureTaskIsStarted() { Contract.Ensures(Contract.Result<Task>() != null); Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created); } } [ContractClass(typeof(ContractClassForIFoo))] public interface IFoo { Task<int> MethodAsync(int val); } [ContractClassFor(typeof(IFoo))] internal abstract class ContractClassForIFoo : IFoo { public Task<int> MethodAsync(int val) { Contract.Requires(val >= 0); ContractsAbbreviators.EnsureTaskIsStarted(); Contract.Ensures(Contract.Result<int>() == val); Contract.Ensures(Contract.Result<int>() >= 5); Contract.Ensures(Contract.Result<int>() < 10); throw new NotImplementedException(); } } public class FooContractFailTask : IFoo { public Task<int> MethodAsync(int val) { return new Task<int>(() => val); // esnure raises exception // Contract.Ensures(Contract.Result<Task>().Status != TaskStatus.Created); } } public class FooContractFailTaskResult : IFoo { public async Task<int> MethodAsync(int val) { await Task.Delay(val).ConfigureAwait(false); return val + 1; // esnure raises exception // Contract.Ensures(Contract.Result<int>() == val); } } public class Foo : IFoo { public async Task<int> MethodAsync(int val) { const int maxDeapth = 9; await Task.Delay(val).ConfigureAwait(false); if (val < maxDeapth) { await MethodAsync(val + 1).ConfigureAwait(false); } return val; } }