Usando BeginInvoke / EndInvoke em uma forma com vários segmentos. Como AsyncCallback, AsyncWaitHandle e IsCompleted interagem?

votos
4

A resposta de Andreas Huber a esta pergunta me deu uma idéia para implementar Concurrent<T>com delegados assíncronos em vez do ThreadPool. No entanto, eu estou achando mais difícil de entender o que está acontecendo quando um AsyncCallbacké passado para BeginInvoke, especialmente quando vários segmentos têm acesso a IAsyncResult. Infelizmente, neste caso não parece ser coberto pelo MSDN ou em qualquer lugar que eu poderia encontrar. Além disso, todos os artigos que eu poderia encontrar ou foram escritos antes de encerramentos e genéricos estavam disponíveis ou apenas parece que maneira. Há várias perguntas (e respostas que eu espero que sejam verdadeiras, mas eu estou pronto para se decepcionar):

1) Será que usando um fecho como um AsyncCallback fazer alguma diferença?
(Espero que não)
2) Se a thread espera no AsyncWaitHandle, vai ser assinalado
a) antes do retorno de chamada inicia ou b) depois de terminar?
(Espero b)
3) Enquanto o retorno de chamada está sendo executado, o que vai IsCompletedvoltar? Possibilidades eu posso ver:
a) true; b) false; c) falseantes da chamada de retorno chama EndInvoke, truedepois.
(Espero b ou c)
4) Vai DisposedObjectExceptionser lançada se alguns thread espera no AsyncWaitHandledepois EndInvokeé chamado?
(Esperemos que não, mas espero que sim).

Desde que as respostas são como espero, isso parece que deve funcionar:

public class Concurrent<T> { 
    private IAsyncResult _asyncResult;
    private T _result;

    public Concurrent(Func<T> f) { // Assume f doesn't throw exceptions
        _asyncResult = f.BeginInvoke(
                           asyncResult => {
                               // Assume assignment of T is atomic
                               _result = f.EndInvoke(asyncResult); 
                           }, null);
    }

    public T Result {
        get {
            if (!_asyncResult.IsCompleted)
                // Is there a race condition here?
                _asyncResult.AsyncWaitHandle.WaitOne();
            return _result;  // Assume reading of T is atomic
        }
    ...

Se as respostas às perguntas 1-3 são os que eu esperar, não deve haver nenhuma condição raace aqui, tanto quanto eu posso ver.

Publicado 02/01/2009 em 15:30
fonte usuário
Em outras línguas...                            


2 respostas

votos
2

Eu estive olhando para chamadas assíncronas apenas recentemente. Eu encontrei um ponteiro para um artigo com um exemplo de implementação de um IAsyncResult pelo respeitado autor Jeffrey Richter. Eu aprendi muito sobre como async chamadas trabalhar estudando essa implementação.

Você também pode ver se você pode baixar e examinar o código fonte para o System.Runtime.Remoting.Messaging.AsyncResultque você está especificamente preocupados com. Aqui está um link para obter instruções sobre como fazer isso no Visual Studio .

Para adicionar um pouco de boas respostas de JaredPar ...

1: Eu acredito que se você definir um fecho que pode ser atribuído a uma variável do tipo AsyncCallback (leva um IAsyncResult e retorna void) ele deve funcionar como seria de esperar um fechamento para trabalhar como esse delegado, mas eu não tenho certeza se há poderia haver problemas de escopo. O escopo local de origem deveria ter retornado muito antes do retorno de chamada é invocado (que é o que faz com que seja uma operação assíncrona), para ter isso em mente no que diz respeito às referências ao (stack) variáveis ​​locais e como isso vai se comportar. Referenciando variáveis ​​membro deve estar bem, eu acho.

2: Eu acho que a partir de seu comentário que você pode ter entendido mal a resposta a esta. No exemplo de implementação de Jeffrey Richter o identificador de espera é sinalizado antes do retorno de chamada é invocado. Se você pensar sobre isso, ele tem que ser desta maneira. Uma vez que invoca o callback ele perde o controle da execução. Suponha que o método de retorno gera uma exceção .... execução poderia relaxar para trás após o método que invocou a chamada de retorno e, assim, evitar que ele nunca mais tarde sinalizando o identificador de espera! Assim, o identificador de espera precisa ser sinalizado antes de retorno de chamada é invocado. Eles também são muito mais próximos no tempo, se feito nessa ordem do que se assinala o identificador de espera somente após o retorno de chamada retornou.

3: Como diz JaredPar, IsCompleted deve ser retornar true antes do retorno de chamada e antes do identificador de espera é sinalizado. Isso faz sentido porque se IsCompleted é falso que seria de esperar a chamada para EndInvokebloquear, e toda a ponto do identificador de espera (como acontece com a chamada de retorno) é saber quando o resultado está pronto e não vai bloquear. Então, primeiro IsCompleted está definido para true, em seguida, o identificador de espera é sinalizada, e então o retorno de chamada é chamado. Veja como exemplo de Jeffrey Richter faz. No entanto , você provavelmente deve tentar evitar suposições sobre a ordem em que esses três métodos (polling, esperar lidar, callback) pode detectar a conclusão, porque é possível implementá-los em uma ordem diferente do que o esperado.

4: Eu não posso ajudá-lo lá, exceto que você pode encontrar a resposta de depuração no código-fonte quadro para a aplicação você está curioso sobre. Ou você provavelmente poderia chegar a um experimento para descobrir ... ou configurar uma boa experiência e depurar na fonte de quadro para ser realmente certo.

Respondeu 02/09/2009 em 09:31
fonte usuário

votos
2

Questão 1

Eu acho que parte do problema é equívoco. IAsyncResult não é acessado de vários segmentos, a menos que você passá-lo explicitamente para um. Se você olhar para a implementação para mos Comece *** estilo API do BCL, você vai notar a IAsyncResult é sempre apenas criado e destruído a partir do segmento onde o Begin *** ou End *** chamada realmente ocorrer.

Questão 2

AsyncWaitHandle deve ser sinalizado após a operação está 100% concluída.

Questão 3

IsCompleted deve retornar true quando a operação subjacente é completo (não mais trabalho a fazer). A melhor maneira de ver IsComplete é que se o valor for

  1. true -> Chamada End *** retornará imediatamente
  2. false -> Callind End *** irá bloquear por algum período de tempo

pergunta 4

Esta é dependente da implementação. Não há nenhuma maneira de realmente dar uma resposta cobertor aqui.

amostras

Se você está interessado em uma API que permite que você facilmente executar um delegado em outro segmento e acessar o resultado quando terminar, confira o meu Utility Biblioteca RantPack . Ele está disponível na fonte e em formato binário. Ele tem um totalmente clarificado, API Futuro que permite o funcionamento simultâneo de delegados.

Além disso, há uma implementação de IAsyncResult que cobre a maior parte das perguntas neste post.

Respondeu 02/01/2009 em 15:46
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more