Pergunta

Parece que chamar Invoke em um controle winforms em um retorno de chamada de um vazamento System.Threading.Timer alças até que o temporizador é descartado. Alguém tem uma idéia de como resolver isso? Eu preciso pesquisa para um valor a cada segundo e atualizar a interface do usuário em conformidade.

Eu tentei-o em um projeto de teste para se certificar de que aquela era realmente a causa do vazamento, que é simplesmente o seguinte:

    System.Threading.Timer timer;
    public Form1()
    {
        InitializeComponent();
        timer = new System.Threading.Timer(new System.Threading.TimerCallback(DoStuff), null, 0, 500);
    }
    void DoStuff(object o)
    {
        this.Invoke(new Action(() => this.Text = "hello world"));
    }

Este vai vazar 2 alças / segundo, se você assistir no gerenciador de tarefas do Windows.

Foi útil?

Solução

Invoke age como um par BeginInvoke / EndInvoke em que ele envia a mensagem para o segmento interface do usuário, cria uma alça, e espera nesse punho para determinar quando o método invocado é completa. É este identificador que é "vazamento". Você pode ver que estes são eventos sem nome usando Explorador para monitorar as alças enquanto a aplicação está em execução.

Se IAsyncResult foi IDisposable, a eliminação do objeto iria cuidar de limpar a alça. Como não é, as alças se limpar quando o coletor de lixo é executado e chama o finalizador do objeto IAsyncResult. Você pode ver isso adicionando um GC.Collect () após cada 20 chamadas para DoStuff - a contagem de identificador cai a cada 20 segundos. Claro, "resolver" o problema adicionando chamadas para GC.Collect () é o errado maneira de abordar a questão; deixe o coletor de lixo fazer o seu próprio trabalho.

Se não o fizer necessidade a chamada Invoke para ser síncrono, o uso BeginInvoke em vez de invocar e não chamar EndInvoke; o resultado final vai fazer a mesma coisa, mas sem alças serão criadas ou "vazou".

Outras dicas

Existe algum motivo você não pode usar uma System.Windows.Forms.Timer aqui? Se o temporizador está ligado a essa forma que você não precisa sequer invocar.

Ok, eu dei-lhe um pouco de tempo mais e parece que ele realmente não está vazando alças, é apenas a natureza indeterminada do coletor de lixo. Eu cruzei-lo caminho até 10ms por carrapato e que ia subir muito rápido e 30 segundos mais tarde viria a cair de volta para baixo.

Para confirmar a teoria que eu chamada manualmente GC.Collect () em cada chamada de retorno (não faça isso em projetos reais, este foi apenas para teste, há numerosos artigos lá fora, sobre o porquê que é uma má idéia) e a contagem de identificador era estável.

Interessante -. Isto não é uma resposta, mas com base no comentário de Andrei eu teria pensado que isso não iria vazar alças da mesma maneira no entanto ele faz alças de vazamento na mesma taxa do OP mencionado

System.Threading.Timer timer;
    public Form2()
    {
        InitializeComponent();

    }

    private void UpdateFormTextCallback()
    {
        this.Text = "Hello World!";
    }

    private Action UpdateFormText;

    private void DoStuff(object value)
    {
        this.Invoke(UpdateFormText);
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        timer = new System.Threading.Timer(new TimerCallback(DoStuff), null, 0, 500);
        UpdateFormText = new Action(UpdateFormTextCallback);
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top