Pergunta

Eu tenho tentado aprender a programação de multi-threaded em C # e eu estou confuso sobre quando é melhor usar um pool de threads vs. criar meus próprios tópicos. Um livro recomenda o uso de um pool de threads para apenas pequenas tarefas (whatever that means), mas eu não consigo encontrar quaisquer orientações real. Quais são algumas das considerações que você usa quando tomar essa decisão de programação?

Foi útil?

Solução

Se você tem muitas tarefas lógicas que requerem processamento constante e você quer que isso seja feito em uso paralelo da piscina + programador.

Se você precisa para fazer suas tarefas relacionadas IO simultaneamente como o download de material a partir de servidores remotos ou acesso ao disco, mas necessidade de fazer isso digamos uma vez a cada poucos minutos, em seguida, fazer suas próprias threads e matá-los uma vez que você está acabado.

Edit:. Sobre algumas considerações, eu uso pools de threads para acesso de banco de dados, física / simulação, AI (jogos), e para tarefas de script correu em máquinas virtuais que muitos processos de usuário tarefas definidas

Normalmente, um pool composto por 2 threads por processador (tão provável 4 hoje em dia), no entanto, você pode configurar a quantidade de tópicos que você quer, se você sabe quantos você precisa.

Edit: A razão para fazer suas próprias threads é por causa de mudanças de contexto, (isso é quando tópicos precisa trocar dentro e fora do processo, juntamente com a sua memória). Tendo mudanças de contexto inúteis, dizer quando você não estiver usando seus segmentos, apenas deixá-los sentar-se em torno de como se poderia dizer, pode facilmente metade do desempenho do seu programa (digamos que você tem 3 linhas de dormir e 2 tópicos ativos). Assim, se os fios de download está apenas esperando que eles estão comendo toneladas de CPU e arrefecimento a cache do seu aplicativo real

Outras dicas

Eu sugiro que você use um pool de threads em C # para as mesmas razões que qualquer outra língua.

Quando você quiser limitar o número de threads em execução ou não querem a sobrecarga de criar e destruir-los, use um pool de threads.

Por pequenas tarefas, o livro que você leu tarefas meios com um tempo de vida curto. Se demorar dez segundos para criar um segmento que é executado somente por um segundo, que é um lugar onde você deve estar usando piscinas (ignore os meus valores reais, é a razão que conta).

Caso contrário, você passar a maior parte do seu tempo a criar e destruir threads ao invés de simplesmente fazer o trabalho que eles estão destinados a fazer.

Aqui está um resumo agradável do pool de threads em .Net: http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx

O posto também tem alguns pontos sobre quando você não deve usar o pool de threads e iniciar o seu próprio segmento em vez.

Eu recomendo a leitura do este e-book gratuito: Enfiar em C # por Joseph Albahari

Pelo menos Leia a seção "Introdução". O e-book oferece uma ótima introdução e inclui uma riqueza de informações avançadas de segmentação também.

saber se deve ou não usar o pool de threads é apenas o começo. Em seguida, você precisará determinar qual o método de entrar no pool de threads melhor se adapte às suas necessidades:

  • Task Parallel Library (.NET Framework 4.0)
  • ThreadPool.QueueUserWorkItem
  • Os delegados assíncronos
  • BackgroundWorker

Este endereço de e-book explica todos estes e aconselha quando usá-los vs. criar o seu próprio segmento.

O pool de threads é projetado para reduzir a troca de contexto entre os seus segmentos. Considere um processo que tem várias componentes em execução. Cada um desses componentes poderia ser a criação de threads de trabalho. Os mais threads em seu processo, mais tempo é desperdiçado em troca de contexto.

Agora, se cada um desses componentes estavam na fila itens para o pool de threads, você teria muito menos troca de contexto sobrecarga.

O pool de threads é projetado para maximizar o trabalho que está sendo feito através de seus CPUs (ou núcleos de CPU). É por isso que, por padrão, o pool de threads gira múltiplas threads por processador.

Existem algumas situações em que você não gostaria de usar o pool de threads. Se você está à espera de I / O, ou à espera de um evento, etc, então você amarrar que thread do pool e não pode ser usado por qualquer pessoa. Mesma idéia se aplica às tarefas de execução longa, embora o que constitui uma tarefa de longa duração é subjetiva.

Pax Diablo faz um bom ponto também. Girando tópicos não é livre. É preciso tempo e eles consomem memória adicional para o seu espaço de pilha. O pool de threads irá reutilizar os tópicos para amortizar esse custo.

Nota: você perguntou sobre usando um thread do pool de download de dados ou executar disco I / O. Você não deve usar um thread do pool para isso (pelas razões que descrevi acima). Em vez disso use E / S assíncrona (aka os métodos BeginXX e EndXX). Para uma FileStream que seria BeginRead e EndRead. Para uma HttpWebRequest que seria BeginGetResponse e EndGetResponse. Eles são mais complicado de usar, mas eles são a maneira apropriada de executar multi-threaded I / O.

Cuidado com o pool de threads NET para operações que possam bloquear para qualquer significativa, variável ou parte desconhecida do seu processamento, como é propenso a enfiar fome. Considere utilizando as extensões paralelas NET, que proporcionam um bom número de captações lógicos mais operações roscados. Eles também incluem um novo planejador, que deve ser uma melhoria em ThreadPool. Consulte aqui

Uma das razões para usar o pool de threads para pequenas tarefas só é que há um número limitado de threads do pool. Se um é usado por um longo tempo, então ele pára esse segmento seja usado por outro código. Se isso acontecer muitas vezes em seguida, o pool de threads pode tornar-se esgotado.

Usando o pool de threads pode ter efeitos sutis -. Alguns contadores .NET usar threads do pool e não vai incêndio, por exemplo

Se você tem uma tarefa em segundo plano que vai viver por um longo tempo, como por toda a vida útil de sua aplicação, em seguida, criar o seu próprio segmento é uma coisa razoável. Se houver trabalhos curtos que precisa ser feito em um segmento, então pool de segmentos uso.

Em uma aplicação onde você está criando muitos segmentos, a sobrecarga de criar os tópicos torna-se substancial. Usando o pool de thread cria os fios uma vez e os reutiliza, evitando assim a criação de thread em cima.

Em um aplicativo que eu trabalhei, passando de criar tópicos de usar o pool de threads para o curto tópicos vividas realmente helpped o meio put da aplicação.

Para o mais alto desempenho com unidades executando concorrentemente, escreva seu próprio pool de threads, onde um pool de objetos de Tópicos são criados no arranque e ir para o bloqueio (anteriormente suspensa), à espera de um contexto de execução (um objeto com um padrão interface implementada pelo seu código).

Assim, muitos artigos sobre Tarefas vs. Threads vs .NET ThreadPool não conseguem realmente dar-lhe o que você precisa para tomar uma decisão para o desempenho. Mas quando você compará-los, Threads vencer e, especialmente, um pool de threads. Eles são distribuídos a melhor em toda CPUs e começam-se mais rápido.

O que deve ser discutido é o fato de que a unidade de execução principal do Windows (incluindo o Windows 10) é um segmento e contexto OS comutação sobrecarga é normalmente insignificante. Basta colocar, eu não tenho sido capaz de encontrar provas convincentes de muitos desses artigos, se as reivindicações artigo maior desempenho salvando troca de contexto ou melhor uso da CPU.

Agora, para um pouco de realismo:

A maioria de nós não vai precisar de nosso aplicativo para ser determinística, ea maioria de nós não tem um fundo duro-choques com fios, que, por exemplo, muitas vezes vem com o desenvolvimento de um sistema operacional. O que eu escrevi acima não é para um iniciante.

Então, o que pode ser mais importante é discutir é o que é fácil de programar.

Se você criar seu próprio pool de threads, você vai ter um pouco de escrita para fazer o que você precisa se preocupar com acompanhamento de status de execução, como simular suspender e retomar, e como cancelar a execução - incluindo um toda a aplicação fechada para baixo. Você também pode ter que se preocupar com se você quer crescer de forma dinâmica a sua piscina e também o limite de capacidade da sua piscina terá. Eu posso escrever um tal quadro em uma hora, mas isso é porque eu fiz isso tantas vezes.

Talvez a maneira mais fácil de escrever uma unidade de execução é usar um Task. A beleza de uma tarefa é que você pode criar um e chutá-la fora em linha em seu código (embora cautela pode ser garantido). Você pode passar um token de cancelamento para lidar com quando você quiser cancelar a tarefa. Além disso, ele usa a abordagem promessa de eventos encadeamento, e você pode tê-lo retornar um tipo específico de valor. Além disso, com assíncrona e esperar, existem mais opções e seu código será mais portátil.

Em essência, é importante entender os prós e contras com tarefas vs. Threads contra o .NET ThreadPool. Se eu precisar de alta performance, eu vou usar threads, e eu prefiro usar minha própria piscina.

Uma maneira fácil de comparar é começar-se 512 Threads, 512 Tarefas e 512 tópicos ThreadPool. Você vai encontrar um atraso no início com Threads (daí, por que escrever um pool de threads), mas todos os 512 Threads estará funcionando em poucos segundos, enquanto Tarefas e .NET ThreadPool threads levar até alguns minutos a todos começo.

A seguir estão os resultados de teste de tal (quad core i5 com 16 GB de RAM), dando a cada 30 segundos para ser executado. Os executa código executado simples arquivo I / O em uma unidade SSD.

Resultados do teste

Thread pools são grandes quando você tem mais tarefas para processo de threads disponíveis.

Você pode adicionar todas as tarefas de um pool de threads e especificar o número máximo de segmentos que podem ser executados em um determinado momento.

Confira desta página no MSDN : http://msdn.microsoft.com/en-us /library/3dasc8as(VS.80).aspx

Sempre usar um pool de threads, se puder, trabalho ao mais alto nível de abstração possível. Thread pools esconder criar e destruir threads para você, esta é geralmente uma coisa boa! ??

Na maioria das vezes você pode usar a piscina como você evitar o dispendioso processo de criar a linha.

No entanto, em alguns cenários, você pode querer criar um thread. Por exemplo, se você não é o único a usar o pool de threads e o fio que você cria é longa vida (evitar consumir recursos compartilhados) ou, por exemplo, se você quiser controlar o tamanho da pilha do fio.

Não se esqueça de investigar o trabalho de fundo.

I encontrar para uma série de situações, dá-me apenas o que eu quiser sem o trabalho pesado.

Felicidades.

Eu costumo usar o ThreadPool sempre que eu preciso apenas fazer algo em outro segmento e realmente não me importo quando ele é executado ou termina. Algo como o registo ou talvez mesmo fundo download de um arquivo (embora há melhores maneiras de fazer isso de estilo assíncrona). Eu uso o meu próprio segmento quando eu precisar de mais controle. Também o que eu encontrei está usando uma fila Threadsafe (cortar o seu próprio) para armazenar "comando objetos" é bom quando eu tenho vários comandos que eu preciso para trabalhar em em> 1 fio. Então você pode dividir um arquivo XML e colocar cada elemento em uma fila e, em seguida, ter vários segmentos de trabalho em fazer algum processamento sobre esses elementos. Eu escrevi tal uma fila caminho de volta em uni (VB.net!) Que eu convertido em C #. Eu incluí-lo abaixo para nenhuma razão particular (este código pode conter alguns erros).

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

namespace ThreadSafeQueue {
    public class ThreadSafeQueue<T> {
        private Queue<T> _queue;

        public ThreadSafeQueue() {
            _queue = new Queue<T>();
        }

        public void EnqueueSafe(T item) {
            lock ( this ) {
                _queue.Enqueue(item);
                if ( _queue.Count >= 1 )
                    Monitor.Pulse(this);
            }
        }

        public T DequeueSafe() {
            lock ( this ) {
                while ( _queue.Count <= 0 )
                    Monitor.Wait(this);

                return this.DeEnqueueUnblock();

            }
        }

        private T DeEnqueueUnblock() {
            return _queue.Dequeue();
        }
    }
}

Eu queria um pool de segmentos para distribuir o trabalho em toda a núcleos com tão pouco latência possível, e que não tem que jogar bem com outras aplicações. Eu achei que o desempenho pool de threads .NET não era tão bom quanto poderia ser. Eu sabia que queria um thread por core, então eu escrevi minha própria classe substituto pool de threads. O código é fornecido como uma resposta a outra pergunta StackOverflow aqui .

Quanto à questão original, o pool de threads é útil para quebrar cálculos repetitivos em partes que podem ser executadas em paralelo (assumindo que eles podem ser executados em paralelo, sem alterar o resultado). gerenciamento de threads manual é útil para tarefas como UI e IO.

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