NET: Como ter sinal discussão de fundo principais dados de segmento está disponível?

StackOverflow https://stackoverflow.com/questions/122882

Pergunta

O que é a técnica adequada para ter ThreadA sinal de ThreadB de algum evento, sem ter ThreadB sentar bloqueados à espera de um evento acontecer?

i ter uma discussão de fundo que estará preenchendo uma lista compartilhada . Estou tentando encontrar uma maneira de forma assíncrona sinalizar o thread "main" que não há dados disponíveis para ser apanhada.


i considerado definindo um evento com um objeto EventWaitHandle, mas eu não posso ter o meu thread principal sentado em uma Event.WaitOne ().


i considerada ter um callback delegado, mas a) eu não quero o segmento principal fazendo um trabalho na delegado: as necessidades de rosca para voltar ao trabalho adicionando mais coisas - eu não quero que ele espera, enquanto executa a delegar e b) as necessidades de delegado para ser empacotado para o segmento principal, mas eu não estou correndo uma interface de usuário, eu não tenho controle para .Invoke o delegado contra.


i considerada ter um callback delegado que simplesmente inicia uma System.Windows.Forms.Timer intervalo de zero (com acesso thread para o temporizador sincronizado). Desta forma, o segmento só precisa ser preso como ele chama

Timer.Enabled = true;

mas que parece ser um hack.

Nos tempos antigos meu objeto teria criado uma janela oculta e tinha as mensagens segmento pós para HWND que janelas escondidas. i considerada como criar um controle escondido, mas entendo que você não pode .Invoke em um controle sem alça criado. Além disso, eu não tenho nenhuma UI: meu objeto poderia ter sido criado em um servidor web, serviço ou console, eu não quero que haja um controle gráfico aparecer - nem quero compilar uma dependência System.Windows. formas.


i considerou ter meu objeto expor uma interface ISynchronizeInvoke, mas então eu teria que implementar .Invoke (), e esse é o meu problema.


O que é a técnica adequada para ter fio Um fio de sinal B de algum evento, sem ter segmento B sit bloqueados à espera de um evento acontecer?

Foi útil?

Solução

Aqui está um exemplo de código para a classe System.ComponentModel.BackgroundWorker.

    private static BackgroundWorker worker = new BackgroundWorker();
    static void Main(string[] args)
    {
        worker.DoWork += worker_DoWork;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.WorkerReportsProgress = true;

        Console.WriteLine("Starting application.");
        worker.RunWorkerAsync();

        Console.ReadKey();
    }

    static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Console.WriteLine("Progress.");
    }

    static void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Starting doing some work now.");

        for (int i = 0; i < 5; i++)
        {
            Thread.Sleep(1000);
            worker.ReportProgress(i);
        }
    }

    static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("Done now.");
    }

Outras dicas

Estou combinando algumas respostas aqui.

A situação ideal usa uma bandeira thread-safe, como um AutoResetEvent. Você não tem que bloquear indefinidamente quando você chamar WaitOne(), na verdade ele tem uma sobrecarga que permite que você especifique um tempo limite. Essa sobrecarga retornos false se o sinalizador não foi definido durante o intervalo.

A Queue é uma estrutura mais ideal para um relacionamento produtor / consumidor, mas você pode imitá-los se suas exigências estão forçando você a usar um List. A principal diferença é que você vai ter para garantir o seu acesso fechaduras de consumo para a coleção enquanto ele está extraindo itens; a coisa mais segura é provavelmente a usar o método CopyTo para copiar todos os elementos para uma matriz, em seguida, liberar o bloqueio. É claro, garantir que o seu produtor não tentará atualizar o List enquanto o bloqueio é mantido.

Aqui está uma simples C # aplicação de consola que demonstra como isso pode ser implementado. Se você brincar com os intervalos de tempo pode causar várias coisas aconteçam; nesta configuração particular que eu estava tentando ter o produtor gerar vários itens antes de os controlos de consumo para os itens.

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        private static object LockObject = new Object();

        private static AutoResetEvent _flag;
        private static Queue<int> _list;

        static void Main(string[] args)
        {
            _list = new Queue<int>();
            _flag = new AutoResetEvent(false);

            ThreadPool.QueueUserWorkItem(ProducerThread);

            int itemCount = 0;

            while (itemCount < 10)
            {
                if (_flag.WaitOne(0))
                {
                    // there was an item
                    lock (LockObject)
                    {
                        Console.WriteLine("Items in queue:");
                        while (_list.Count > 0)
                        {
                            Console.WriteLine("Found item {0}.", _list.Dequeue());
                            itemCount++;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("No items in queue.");
                    Thread.Sleep(125);
                }
            }
        }

        private static void ProducerThread(object state)
        {
            Random rng = new Random();

            Thread.Sleep(250);

            for (int i = 0; i < 10; i++)
            {
                lock (LockObject)
                {
                    _list.Enqueue(rng.Next(0, 100));
                    _flag.Set();
                    Thread.Sleep(rng.Next(0, 250));
                }
            }
        }
    }
}

Se você não deseja bloquear o produtor em tudo, é um pouco mais complicado. Neste caso, eu sugiro fazer o produtor a sua própria classe com tanto um privado e um buffer pública e uma AutoResetEvent público. O produtor vai por itens de armazenamento padrão no buffer privado, em seguida, tentar escrevê-los para o buffer público. Quando o consumidor está trabalhando com o tampão público, ele redefine o sinalizador no objeto produtor. Antes das tentativas produtores para mover itens a partir do buffer privada para o buffer público, verifica esta bandeira e apenas copia itens quando o consumidor não está trabalhando nisso.

Se você usar um backgroundworker para iniciar a segunda linha e usar o evento ProgressChanged para notificar o outro segmento que dados estão prontos. Outros eventos também estão disponíveis. este artigo MSDN deve começar .

Existem muitas maneiras de fazer isso, dependendo exatamente o que você quer fazer. A produtor / consumidor fila é provavelmente o que você quer. Para um olhar excelente em profundidade em tópicos, consulte o capítulo sobre Enfiar (disponível online) do excelente livro C # 3.0 in a Nutshell .

Você pode usar um AutoResetEvent (ou ManualResetEvent). Se você usar AutoResetEvent.WaitOne (0, false), não irá bloquear. Por exemplo:

AutoResetEvent ev = new AutoResetEvent(false);
...
if(ev.WaitOne(0, false)) {
  // event happened
}
else {
 // do other stuff
}
classe

O BackgroundWorker é a resposta neste caso. É a construção única enfiar que é capaz de mensagens de forma assíncrona enviar para o fio que criou o objeto BackgroundWorker. Internamente BackgroundWorker usa a classe AsyncOperation chamando o método asyncOperation.Post().

this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.asyncOperation.Post(delegateMethod, arg);

Algumas outras classes no .NET framework também usam AsyncOperation:

  • BackgroundWorker
  • SoundPlayer.LoadAsync ()
  • SmtpClient.SendAsync ()
  • Ping.SendAsync ()
  • WebClient.DownloadDataAsync ()
  • WebClient.DownloadFile ()
  • WebClient.DownloadFileAsync ()
  • WebClient ...
  • PictureBox.LoadAsync ()

Se o seu thread "main" é a bomba de mensagem do Windows (GUI) da linha, então você pode pesquisar usando um Forms.Timer - ajustar o temporizador de intervalo de acordo com o quão rápido você precisa ter seu segmento GUI 'aviso' os dados do o segmento de trabalho.

Lembre-se de acesso de sincronização para o List<> compartilhada se você está indo para uso foreach, a exceções evitar CollectionModified.

Eu uso essa técnica por todas as atualizações de GUI adaptadas ao mercado de-dados em um aplicativo de negociação em tempo real, e ele funciona muito bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top