Pergunta

Eu tenho um aplicativo WPF um tanto complexo que parece estar 'pendurado' ou ficar preso em uma chamada de espera ao tentar usar o despachante para invocar uma chamada no thread da interface do usuário.

O processo geral é:

  1. Lidar com o evento de clique em um botão
  2. Crie um novo thread (STA) que: Cria uma nova instância do apresentador e da interface do usuário, então chama o método desconectar
  3. Desconectar então define uma propriedade na interface do usuário chamada Nome
  4. O Setter for Name usa o código a seguir para definir a propriedade:

    if(this.Dispatcher.Thread != Thread.CurrentThread)
    {
        this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
            this.Name = value; // Call same setter, but on the UI thread
        });
        return;
    }

    SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly.

Meu problema é que quando o despachante invocar O método é chamado, parece pendurar todas as vezes, e a pilha de chamadas indica que está dormindo, aguarde ou participe da implementação da Invoke.

Então, há algo que estou fazendo de errado que estou perdendo, óbvio ou não, ou existe uma maneira melhor de chamar o tópico da interface do usuário para definir esta propriedade (e outros)?

Editar: A solução foi chamar System.Windows.Threading.dispatcher.run () no final do delegado do thread (por exemplo, onde o trabalho estava sendo realizado) - graças a todos que ajudaram.

Foi útil?

Solução

Você diz que está criando um novo thread STA, o despachante está em execução?

Estou recebendo de "this.dispatcher.thread! = Thread.currentThread" que você espera que seja um despachante diferente. Certifique -se de que esteja em execução, caso contrário, não processará sua fila.

Outras dicas

Invoke é síncrono - você deseja despachante.begininvoke. Além disso, acredito que seu exemplo de código deve mover a instrução "setValue" dentro de um "else".

Eu acho que isso é melhor mostrado com código. Considere este cenário:

Tópico A faz isso:

lock (someObject)
{
   // Do one thing.
   someDispatcher.Invoke(() =>
   {
      // Do something else.
   }
}

Tópico B faz isso:

someDispatcher.Invoke(() =>
{
   lock (someObject)
   {
      // Do something.
   }
}

Tudo pode parecer bom e elegante à primeira vista, mas não é. Isso produzirá um impasse. Os despachantes são como filas para um tópico e, ao lidar com impasse como esses, é importante pensar neles dessa maneira: "O que o despacho anterior poderia ter tido minha fila?". A linha A entrará ... e despachará sob uma fechadura. Mas, e se o Thread B chegar no momento em que o tópico A está no código marcado "Do One Thing"? Nós iremos...

  • O thread A possui a trava em algum objeto e está executando algum código.
  • O Thread B agora despacha, e o despachante tentará obter o bloqueio em algum objeto, batendo no seu despachante, pois o Thread A já possui esse bloqueio.
  • Thread A Will então fila outro item de expedição. Este item nunca será demitido, porque seu despachante nunca terminará de processar sua solicitação anterior; já está preso.

Agora você tem um lindo impasse.

Eu acho que você quer dizer se (! This.dispatcher.checkaccess ())

Eu também estou pegando um jeito com invocação, ou se posso começar, meu delegado não está sendo chamado - parece estar fazendo tudo pelo livro :-(

Isso soa como um impasse; Isso normalmente aconteceria se a chamada do encadeamento .invoke já tivesse um bloqueio / mutex / etc que o thread da interface do usuário precisa concluir seu trabalho. A abordagem mais simples seria usar o BeginInvoke: dessa maneira, o thread atual pode continuar funcionando e (presumivelmente) soltará o bloqueio em breve - permitindo que a interface do usuário a adquira. Como alternativa, se você conseguir identificar o bloqueio ofensivo, poderá liberá -lo deliberadamente por uma duração.

Estou tendo um problema semelhante e, embora ainda não tenha certeza de qual é a resposta, acho que seu

 if(this.Dispatcher.Thread != Thread.CurrentThread)
{
    this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
        this.Name = value; // Call same setter, but on the UI thread
    });
    return;
}

deve ser substituído por

 if(this.Dispatcher.CheckAccess())
{
    this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
        this.Name = value; // Call same setter, but on the UI thread
    });
    return;
}

O CheckAccess não aparecerá no IntelliSense, mas está lá e destinado a esse fim. Além disso, concordo que, em geral, você quer começar aqui, no entanto, descobri que não recebo atualizações de interface do usuário quando faço isso assíncrono. Infelizmente, quando faço isso de maneira síncrona, recebo uma condição de impasse ...

Eu sei que este é um tópico antigo, mas aqui está outra solução.

Acabei de resolver um problema semelhante. Meu despachante estava funcionando bem, então ...

Eu tive que mostrar a janela Debug -> Thread para identificar todos os threads que estão executando meu código em qualquer lugar.

Ao verificar cada um dos fios, vi rapidamente qual fio causou o impasse.

Foram vários tópicos combinando um lock (locker) { ... } declaração e chamadas para despactor.invoke ().

No meu caso, eu poderia apenas mudar um específico lock (locker) { ... } declaração e substitua -a por um Interlocked.Increment(ref lockCounter).

Isso resolveu meu problema porque o impasse foi evitado.

void SynchronizedMethodExample() {

    /* synchronize access to this method */
    if (Interlocked.Increment(ref _lockCounter) != 1) { return; }

    try {
    ...
    }
    finally {
        _mandatoryCounter--;
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top