Quando optamos por utilizar um proxy que fornece apenas a versão síncrona de uma operação, devemos ter cuidado quando efetuamos a chamada para a mesma através de múltiplas threads do lado do cliente.
Ao invocar uma operação do proxy, raramente invocamos o método Open do mesmo, que tem a finalidade de abrí-lo explicitamente, obrigando ao runtime do WCF assegurar essa abertura, garantindo que a conexão seja estabelecida antes da mensagem ser enviada para o respectivo serviço. Isso quer dizer que nenhuma mensagem será enviada até que o proxy esteja completamente aberto.
Como estamos em um ambiente multi-threading do lado do cliente e postergando a abertura do proxy somente a partir da primeira chamada para uma operação, teremos aqui um comportamento que poderá causar alguma confusão. Como haverá múltiplas threads executando em paralelo utilizando o mesmo proxy, uma dessas threads ganhará a possibilidade de abrí-lo e, conseqüentemente, enviar a sua requisição para o serviço. Mesmo que o ambiente seja multi-threading, as outras threads que também tem como tarefa a execução de alguma operação ligada ao mesmo proxy, irão aguardar até que a primeira chamada seja completamente realizada.
Todas as requisições que forem realizadas enquanto a primeira delas (a responsável pela aberta do proxy) estiver em execução, serão armazenadas em uma fila (FIFO), executando sequencialmente cada uma dessas requisições solicitadas enquanto a primeira estava sendo processada. As classes (não documentadas) ServiceChannel e CallOnceManager (namespace System.ServiceModel.Channels) são as responsáveis por garantir essa sincronização. A classe CallOnceManager fornece um método chamado CallOnce que gerencia múltiplas requisições. Ao chegar uma requisição para este método, ele verificará se é ou não a primeira requisição solicitada ao proxy; caso seja, ele encaminhará para a execução e, caso não seja, ele irá armazenar essa requisição em uma fila.
Para resolvermos esse problema, podemos invocar as chamadas de forma assíncrona ou chamar explicitamente o método Open do proxy, não delegando a abertura do mesmo somente quando a primeira requisição for solicitada. O exemplo abaixo ilustra isso:
public partial class Form1 : Form
{
private int _contador;
private ContratoClient _proxy;
public Form1()
{
InitializeComponent();
this._proxy = new ContratoClient();
this._proxy.Open();
}
private void button1_Click(object sender, EventArgs e)
{
new MethodInvoker(() =>
this._proxy.EnviarMensagem(++this._contador)).BeginInvoke(null, null);
}
}