EndInvoke muda CallContext atual - por quê?

votos
6

Tenho seguinte teste

[Test]
public void aaa()
{
    CallContext.LogicalSetData(aa, 1);

    Action parallelMethod = () => CallContext.LogicalSetData(aa, 2); 
    var r = parallelMethod.BeginInvoke(null, null); 
    parallelMethod.EndInvoke(r);

    Assert.That(CallContext.LogicalGetData(aa), Is.EqualTo(1)); 
}

Alguém pode me dizer por que esse teste está falhando na última linha?

Na verdade, eu sei por que - porque EndInvoke está se fundindo CallContext do método paralelo ao atual - mas eu não entendo a razão para isso.

Para mim, este comportamento é semelhante ao alterar valores de parâmetros método de dentro do método que é chamado :-(

EDIT: eu mudei o meu exemplo de código para usar apenas LogicalGetData e LogicalSetData. Como você pode ver na minha outra pergunta eu quero passar alguns dados para outro segmento, mas eu não esperava que EndInvoke () irá substituir meus valores com aqueles mudado em outro segmento.

Publicado 19/05/2009 em 16:32
fonte usuário
Em outras línguas...                            


2 respostas

votos
7

O comportamento ilustrado por seu exemplo é na verdade por design. O LogicalCallContext é capaz de fluir bidireccionalmente através de uma chamada assíncrono ou uma chamada .NET comunicação remota. Quando você chama EndInvoke, LogicalCallContext do contexto criança é mesclado de volta para o pai de, como você observou. Isso é intencional, para que os chamadores de métodos remotos podem ter acesso a quaisquer valores definidos pelo método remoto. Você pode usar esse recurso para o fluxo de dados de volta da criança, se você gostaria.

Depuração isso com a ajuda do reforço fonte .NET Framework, há comentários explícita para este efeito:

em System.Runtime.Remoting.Proxies.RemotingProxy.Invoke:

    case Message.EndAsync: 
         // This will also merge back the call context
         // onto the thread that called EndAsync
         RealProxy.EndInvokeHelper(m, false);

em System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper:

    // Merge the call context back into the thread that
    // called EndInvoke 
    CallContext.GetLogicalCallContext().Merge(
         mrm.LogicalCallContext);

Se você quiser evitar que a intercalação de dados, é muito fácil de ignorar, apenas evitar chamar EndInvoke do thread principal. Você poderia, por exemplo, usar ThreadPool.QueueUserWorkItem, que fluirá a LogicalCallContext em , mas não para fora, ou chamar EndInvoke de um AsyncCallback.

Olhando para o exemplo no site da Microsoft Connect, a razão que você não está vendo o valor get LogicalSetData refluído a chamada RunWorkerCompleted é que BackgroundWorker não flui contexto de volta. Além disso, lembre-se que LogicalSetData não é o mesmo como o armazenamento de segmento local, por isso não importa que RunWorkerCompleted passa a ser executado no segmento interface do usuário - o LogicalCallContext ainda há um contexto criança, e a menos que o pai corre-lo explicitamente volta chamando EndInvoke do fio de desova, que vai ser abandonado. Se você quiser armazenamento local de segmento, você pode acessar a partir de fios, assim:

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Thread.SetData(Thread.GetNamedDataSlot("foo"), "blah!!");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var val = (string)Thread.GetData(Thread.GetNamedDataSlot("foo"));
        MessageBox.Show(val ?? "no value");
    }

Este exemplo aparece um MessageBox exibindo "blá !!". A razão é que ambos os retornos de chamada executado no segmento interface do usuário, por isso, tem acesso à mesma loja segmento local.

Espero que isso ajude esclarecer as coisas.

Respondeu 24/09/2009 em 17:33
fonte usuário

votos
0

É porque você está misturando SetData / GetData com LogicalSetData / LogicalGetData. Há um artigo que você pode ler mais sobre as diferenças entre esses dois par de métodos. A regra de ouro aqui é sempre usar SetData em conjunto com GetData e LogicalSetData em conjunto com LogicalGetData.

Esta modificação vai fazer a sua passagem de teste:

[Test]
public void aaa()
{
    CallContext.SetData("aa", "1");
    Action parallelMethod = () => CallContext.SetData("aa", "2");
    var r = parallelMethod.BeginInvoke(null, null);
    Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
    parallelMethod.EndInvoke(r);
    Assert.That(CallContext.GetData("aa"), Is.EqualTo("1"));
}
Respondeu 19/05/2009 em 16:56
fonte usuário

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