Pergunta

Eu sou geralmente cauteloso em implementar interfaces parcialmente. No entanto, IAsyncResult é um pouco de um caso especial, já que ele suporta vários padrões de uso bastante diferentes. Quantas vezes você usar / ver utilizado o padrão AsyncState / AsyncCallback, ao invés de apenas chamar EndInvoke, usando AsyncWaitHandle, ou IsCompleted polling (eca)?

questão relacionada: detectar que um ThreadPool WorkItem tem concluída / à espera de conclusão .

Considere esta classe (muito aproximada, Fecho necessário):

public class Concurrent<T> {
    private ManualResetEvent _resetEvent;
    private T _result;

    public Concurrent(Func<T> f) {
        ThreadPool.QueueUserWorkItem(_ => {
                                         _result = f();
                                         IsCompleted = true;
                                         if (_resetEvent != null)
                                             _resetEvent.Set();
                                     });
    }

    public WaitHandle WaitHandle {
        get {
            if (_resetEvent == null)
                _resetEvent = new ManualResetEvent(IsCompleted);
            return _resetEvent;
        }

    public bool IsCompleted {get; private set;}
    ...

Tem WaitHandle (preguiçosamente criado, tal como descrito na documentação IAsyncResult) e IsCompleted, mas eu não vejo uma implementação sensata para AsyncState ({return null;}?). Portanto, faz sentido para ele para implementar IAsyncResult? Note-se que Task na biblioteca de extensões Parallel faz implementar IAsyncResult, mas apenas IsCompleted é implementado de forma implícita.

Foi útil?

Solução

  • Na minha experiência, apenas chamar EndInvoke sem qualquer espera ou ser chamado de volta primeira raramente é útil
  • Apenas proporcionando retornos de chamada às vezes não é suficiente, como seus clientes podem querer esperar por várias operações ao mesmo tempo (WaitAny, WaitAll)
  • Eu nunca entrevistados IsCompleted, yuck, de fato! Então, você poderia salvar a implementação de IsCompleted, mas é tão simples que não parece valer a pena potencialmente surpreender seus clientes.

Assim, uma implementação razoável para um método de forma assíncrona pode ser chamado realmente deve fornecer um IAsyncResult totalmente implementado.

BTW, muitas vezes você não precisa implementar IAsyncResult mesmo, apenas devolver o que é devolvido pelo Delegate.BeginInvoke. Veja a implementação de System.IO.Stream.BeginRead para um exemplo.

Outras dicas

Parece que você tem um par de perguntas. Vamos tratá-los individualmente

Criação de WaitHandle preguiçosamente

Sim, esta é a abordagem mais correcta. Você deve fazer isso de uma forma thread-safe, mas preguiçoso é o caminho.

O truque, porém, é descartar o WaitHandle. WaitHandle é uma base de IDisposable e deve ser eliminados de uma forma oportuna. A documentação para IAsycResult não cobre este caso. A melhor maneira de fazer isso é no EndInvoke. A documentação para BeginInvoke afirma explicitamente que, para cada BeginInvoke, deve haver um EndInvoke correspondente (ou BeginRead / EndRead). Este é o melhor lugar em que se desfazer do WaitHandle.

Como deve AsyncState ser implementado?

Se você olhar para as do BCL API padrão que retornam um IAsyncResult, a maioria deles ter um parâmetro de estado. Isto é tipicamente o valor que é retornado de AsyncState (ver a API de soquete é para um exemplo). É uma boa prática incluir uma variável de estado digitado como objeto para qualquer API BeginInvoke API estilo que retorna um IAsyncResult. Não necessário, mas uma boa prática.

No abscence de uma variável de estado, retornando null é aceitável.

IsCompleted API

Esta será altamente dependente da aplicação que cria o IAsyncResult. Mas sim, você deve implementá-lo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top