async & await - sondeo de alternativas [cerrado]

14

Ahora que sabemos lo que está reservado para c # 5, parece que todavía hay una oportunidad para influir en la elección de las dos nuevas palabras clave para ' Asynchrony " que anunció Anders Heijsberg ayer en PDC10 .

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Eric Lippert tiene una explicación de la elección de las dos palabras clave actuales y la forma en que se las ha malinterpretado en los estudios de usabilidad. Los comentarios tienen varias otras proposiciones.

Por favor, una sugerencia por respuesta, se eliminarán los duplicados.

    
pregunta Benjol 29.10.2010 - 22:35

13 respuestas

6

Dado que no tengo claro el significado / necesidad de async , realmente no puedo discutirlo, pero mi mejor sugerencia para reemplazar await es:

yield while (¡mira! no hay palabras clave nuevas)

Nota habiendo pensado un poco más en esto, me pregunto si reutilizar while de esta manera es una buena idea: la tendencia natural sería esperar un booleano después.

(Piensa: encontrar buenas palabras clave es como encontrar buenos nombres de dominio :)

    
respondido por el Benjol 29.10.2010 - 22:41
5

¿Qué hay de no tener una palabra clave?

Me gustaría que el compilador se dé cuenta de que, la mayoría de las veces, cuando llamo a un método asíncrono, quiero el resultado.

Document doc = DownloadDocumentAsync();

Eso es todo. La razón por la que la gente tiene dificultades para pensar una palabra clave para esto es porque es como tener una palabra clave para "hacer lo que harías si las cosas fueran perfectamente normales". Ese debe ser el predeterminado, no requiere una palabra clave.

Actualizar

Originalmente sugerí que el compilador debería ser inteligente con la inferencia de tipos para averiguar qué hacer. Pensando más en esto, mantendría la implementación existente en el CTP tal como está, pero le añadiré un par de triviales, para reducir los casos en los que necesitaría usar la palabra clave await explícitamente.

Inventamos un atributo: [AutoAwait] . Esto solo se puede aplicar a los métodos. Una forma de aplicar esto a su método es marcarlo async . Pero también puedes hacerlo a mano, por ejemplo:

[AutoAwait]
public Task<Document> DownloadDocumentAsync()

Luego, dentro de cualquier método async , el compilador asumirá que desea esperar en una llamada a DownloadDocumentAsync , por lo que no necesita especificarlo. Cualquier llamada a ese método lo esperará automáticamente.

Document doc = DownloadDocumentAsync();

Ahora, si desea "ser inteligente" y obtener el Task<Document> , use un operador start , que solo puede aparecer antes de una llamada de método:

Task<Document> task = start DownloadDocumentAsync();

Limpio, creo. Ahora, una llamada al método simple significa lo que generalmente significa: esperar a que se complete el método. Y start indica algo diferente: no esperes.

Para el código que aparece fuera de un método async , la única forma en que se le permite llamar a un método [AutoAwait] es prefijándolo con start . Esto te obliga a escribir código que tenga el mismo significado, independientemente de que aparezca en un método async o no.

¡Entonces empiezo a ser codicioso! :)

En primer lugar, quiero que async se aplique a los métodos de interfaz:

interface IThing
{
    async int GetCount();
} 

Básicamente significa que el método de implementación debe devolver Task<int> o algo compatible con await , y las personas que llaman al método obtendrán el comportamiento de [AutoAwait] .

También cuando implemento el método anterior, quiero poder escribir:

async int GetCount()

Así que no tengo que mencionar Task<int> como el tipo de retorno.

También, quiero que async se aplique a los tipos de delegado (que, después de todo, son como interfaces con un método). Entonces:

public async delegate TResult AsyncFunc<TResult>();

Un delegado de async tiene, lo has adivinado, el comportamiento de [AutoAwait] . Desde un método async puedes llamarlo y automáticamente será await ed (a menos que elijas solo start it). Y así si dices:

AsyncFunc<Document> getDoc = DownloadDocumentAsync;

Eso simplemente funciona. No es un método llamado. Aún no se ha iniciado ninguna tarea, un async delegate no es una tarea. Es una fábrica para realizar tareas. Puedes decir:

Document doc = getDoc();

Y eso iniciará una tarea y esperará a que termine y le dará el resultado. O puedes decir:

Task<Document> t = start getDoc();

Entonces, un lugar en este donde la "plomería" se filtra es que si desea hacer un delegado a un método async , debe saber usar un tipo async delegate . Entonces, en lugar de Func , debes decir AsyncFunc , y así sucesivamente. Aunque un día, ese tipo de cosas podría solucionarse con una inferencia de tipos mejorada.

Otra pregunta es qué debería suceder si dices inicio en un método ordinario (no asíncrono). Obviamente, un error de compilación sería la opción segura. Pero hay otras posibilidades.

    
respondido por el Daniel Earwicker 30.10.2010 - 19:30
4
hearken unto AsyncFetch(…)

(si no lo consigue, lea la sección de Eric entrada de blog . Al menos es mejor que for sooth Romeo wherefore art thou AsyncFetch(…) )

    
respondido por el Note to self - think of a name 29.10.2010 - 23:04
4

Creo que async está bien, pero tal vez sea porque lo asocio con páginas asíncronas de ASP.NET: la misma idea.

Para la palabra clave await , prefiero continue after o resume after .

No hago como yield o cualquiera de sus variantes, porque la semántica es tal que el método puede que nunca realmente produzca ejecución; Depende del estado de la tarea.

    
respondido por el Aaronaught 29.10.2010 - 23:16
3

También agregué a los comentarios en el blog de Eric, no veo ningún problema con el uso de la misma palabra clave async

var data = async DownloadFileAsync(url);

Solo estoy expresando que quiero descargar el archivo de forma asíncrona. Hay un poco de redundancia aquí, "async" aparece dos veces, porque también está en el nombre del método. El compilador podría ser muy inteligente y detectar la convención de que los métodos que terminan en "Async" son infactos métodos asíncronos, y agregar eso para nosotros en el código compilado. Así que en lugar de eso solo deberías llamar

var data = async DownloadFile(url);

en lugar de llamar al síncrono

var data = DownloadFile(url);

Heck, también deberíamos poder definirlos de la misma manera, ya que la palabra clave async está en nuestra declaración, ¿por qué debemos agregar manualmente "Async" a cada nombre de método? El compilador puede hacerlo por nosotros.

    
respondido por el Mark H 30.10.2010 - 02:00
3

async = tarea : está modificando una función para devolver una tarea, ¿por qué no usar la palabra clave "tarea"?

await = finish : no necesariamente tenemos que esperar, pero la tarea debe "finalizar" antes de usar el resultado.

    
respondido por el John Fisher 07.11.2010 - 03:28
2

Me gusta yield until . yield while , ya sugerido, es genial y no introduce nuevas palabras clave, pero creo que "hasta" captura el comportamiento un poco mejor.

Creo que yield <something> es una gran idea, porque el rendimiento ya captura la idea de hacer que el resto del método sea una continuación tan bien. Tal vez alguien pueda pensar en una palabra mejor que "hasta".

    
respondido por el nlawalker 30.10.2010 - 02:23
2

Solo quiero registrar mi voto para la sugerencia de Aaron G de comefrom - el primer uso apropiado que he visto de de INTERCAL COMEFROM declaración. La idea es que es algo opuesto a GOTO (saltando desde desde la declaración GOTO) en que hace algún lugar en su código para saltar a la declaración COMEFROM.

    
respondido por el Gabe 30.10.2010 - 07:09
2

Ya que estamos tratando con Task<T> s, ¿qué hay de usar start como palabra clave antes de la declaración, como en:

start var document = FetchAsync(urls[i]);

    
respondido por el Protagonist 05.11.2010 - 19:22
1

Vale la pena señalar que F # también usa la palabra clave async en sus flujos de trabajo asíncronos, que es prácticamente lo mismo que la nueva funcionalidad asíncrona en C # 5. Por lo tanto, mantendría ese uno igual

Para la palabra clave await en F #, solo usan let! en lugar de let . C # no tiene la misma sintaxis de asignación, por lo que necesitan algo en el lado derecho del signo = . Como dijo Benjol, funciona igual que yield , así que casi debería ser una variante de eso.

    
respondido por el Scott Whitlock 30.10.2010 - 01:50
1

yield async FetchAsync(..)

Esto va perfectamente con el modificador async que necesitas poner en el método que estás invocando. Y también la semántica del yield return actual, es decir, devuelves y produce la ejecución al código de enumeración, mientras que en este caso estás cediendo tu ejecución al método asíncrono.

Imagínese si en el futuro habrá otros usos para yield , podríamos agregar un yield x donde x es la nueva característica brillante en lugar de tener todas estas palabras clave diferentes para hacer casi lo mismo, ejecución de rendimiento.

Francamente, no entiendo muy bien el argumento de "no ceder la ejecución". Después de todo, ¿el punto de llamar a otro método es ya 'ceder la ejecución' a ese método? ¿Independientemente de si es asíncrono o no? Me estoy perdiendo algo aquí?

Y es bueno para usted si async devuelve sincrónicamente, pero tener la palabra clave allí debería significar que existe la posibilidad de que el método se ejecute de forma asíncrona y de que esté ejecutando otro método. Su método debe tenerlo en cuenta, independientemente de si el método realiza o no llamadas asíncronas.

OMI Creo que los diversos casos de "no rendimiento" son un detalle de implementación. Prefiero responder por la coherencia en el idioma (es decir, reutilizar yield ).

    
respondido por el chakrit 31.10.2010 - 20:11
0

¿Qué hay de complete , como en "Quiero completar la tarea"?

Task<byte[]> downloadTask = DownloadFileAsync(url);
byte[] data = complete downloadTask;
    
respondido por el Allon Guralnek 30.10.2010 - 01:50
0

task (para la declaración del método) y async (dentro del cuerpo del método)

    
respondido por el sblom 24.01.2012 - 05:08

Lea otras preguntas en las etiquetas