Tenho quase 3 anos de experiência escrevendo aplicativos Web em Java usando estruturas MVC (como struts). Eu nunca escrevi código multithread até agora, embora tenha escrito código para as principais redes de varejo.
Recebo algumas perguntas sobre multithreading durante as entrevistas e respondo normalmente (principalmente perguntas simples). Isso me deixou imaginando a importância do Multithreading no cenário atual do setor.
multithreading
user2434
fonte
fonte
Respostas:
Isso é extremamente importante.
O mais importante, porém, é entender que a multithreading é apenas uma maneira de resolver o problema de assincronia. O ambiente técnico no qual muitas pessoas estão escrevendo software difere do ambiente histórico de desenvolvimento de software (de aplicativos monolíticos que executam cálculos em lote) de duas maneiras principais:
Máquinas com muitos núcleos agora são comuns. Não podemos mais esperar que a velocidade do relógio ou a densidade do transistor aumente em ordens de magnitude. O preço da computação continuará caindo, mas cairá por causa de muito paralelismo. Teremos que encontrar uma maneira de tirar proveito desse poder.
Agora, os computadores estão fortemente conectados em rede e os aplicativos modernos contam com a capacidade de buscar informações valiosas de várias fontes.
Do ponto de vista computacional, esses dois fatores se resumem essencialmente à mesma idéia central: as informações estarão cada vez mais disponíveis de maneira assíncrona . Se as informações de que você precisa estão sendo computadas em outro chip da sua máquina ou em um chip do outro lado do mundo, não importa. De qualquer maneira, seu processador está sentado lá, queimando bilhões de ciclos por segundo, aguardando informações quando poderia estar fazendo um trabalho útil.
Portanto, o que importa agora, e o que importa ainda mais no futuro, não é multithreading por si só, mas, ao contrário, lidar com assincronia . A multithreading é apenas uma maneira de fazer isso - uma maneira complicada e propensa a erros que só ficará mais complicada e propensa a erros, à medida que os chips com modelo de memória fraca se tornarem mais amplamente utilizados.
O desafio para os fornecedores de ferramentas é apresentar algo muito melhor do que o multithreading para que nossos clientes lidem com a infraestrutura assíncrona que usarão no futuro.
fonte
concurrency
é mais importante que oasynchronous
comportamento. Você pode ter assincronia sem simultaneidade (isto é, vários threads em uma CPU de núcleo único)asynchronous
não é um substituto semânticoconcurrency
.Está se tornando cada vez mais importante, pois os processadores modernos têm cada vez mais núcleos. Há uma década, a maioria dos computadores existentes tinha apenas um processador; portanto, o multithreading era importante apenas em aplicativos de servidor de última geração. Atualmente, mesmo os laptops básicos possuem processadores multicore. Em alguns anos, até dispositivos móveis ... Então, mais e mais códigos são necessários para usar as vantagens potenciais de desempenho da simultaneidade e executar corretamente em um ambiente multithread.
fonte
Em geral, o multi-threading já é bastante importante e só se tornará mais importante nos próximos anos (como Péter Török) apontou - é como os processadores serão dimensionados para o futuro previsível (mais núcleos em vez de MHz mais alto) .
No seu caso, no entanto, você parece estar trabalhando principalmente com aplicativos da web. Os aplicativos da Web, por natureza, são multiencadeados devido à maneira como o servidor da Web processa solicitações para cada usuário (por exemplo, em paralelo). Embora seja provavelmente importante que você entenda a simultaneidade e a segurança de encadeamentos (especialmente ao lidar com caches e outros dados compartilhados), duvido que você tenha muitos casos em que é benéfico encadear o código de aplicativo da Web internamente (ou seja, vários trabalhadores threads por solicitação). Nesse sentido, acho que ser um especialista em multiencadeamento não é realmente necessário para um desenvolvedor web. É frequentemente solicitado em entrevistas, porque é um assunto bastante complicado, e também porque muitos entrevistadores apenas pesquisam algumas perguntas 10 minutos antes de você chegar lá.
fonte
Multi-threading é um arenque vermelho. O multiencadeamento é um detalhe da implementação do problema real, que é simultaneidade . Nem todos os programas encadeados são simultâneos devido a bloqueios e o que não.
Threads são apenas um modelo e padrão de implementação para implementar
concurrent
programas.Por exemplo, você pode escrever um software altamente escalonável e tolerante a falhas, sem executar multithreading em idiomas como o Erlang.
fonte
Bem, para passar nas entrevistas, o multithreading pode ser bastante importante. Citando a si mesmo , "ao entrevistar candidatos para nossa equipe, faço perguntas de concorrência não porque essas habilidades são importantes em nosso projeto ( não são ), mas porque, de alguma forma, facilitam a avaliação do conhecimento geral da linguagem que usamos ..."
fonte
Entender como alavancar o encadeamento para melhorar o desempenho é uma habilidade essencial no ambiente de software atual, para a maioria dos setores e aplicativos.
No mínimo, entender os problemas envolvidos com a simultaneidade deve ser um dado.
A observação óbvia de que nem todos os aplicativos ou ambientes poderão tirar proveito disso se aplica, por exemplo, em muitos sistemas embarcados. No entanto, parece que o processador Atom (et al) parece estar trabalhando para mudar isso (multicore leve começando a se tornar mais comum).
fonte
Parece que você já está escrevendo código multithread.
A maioria dos aplicativos da web Java pode manipular várias solicitações ao mesmo tempo e faz isso usando vários encadeamentos.
Portanto, eu diria que é importante conhecer pelo menos o básico.
fonte
Ainda é importante em situações em que você precisa, mas, como muitas coisas no desenvolvimento, é a ferramenta certa para o trabalho certo. Passei três anos sem tocar na segmentação, agora praticamente tudo o que faço tem alguns fundamentos. Com os processadores com vários núcleos, ainda existe uma grande necessidade de segmentação, mas todos os motivos tradicionais ainda são válidos, você ainda deseja interfaces responsivas e deseja lidar com a sincronização e continuar com outras coisas ao mesmo tempo.
fonte
Resposta curta: Muito.
Resposta mais longa: Os computadores eletrônicos (baseados em transistor) estão se aproximando rapidamente dos limites físicos da tecnologia. Está ficando cada vez mais difícil extrair mais relógios de cada núcleo enquanto gerencia a geração de calor e os efeitos quânticos dos circuitos microscópicos (os caminhos dos circuitos já estão sendo colocados tão próximos entre os chips modernos que um efeito chamado "tunelamento quântico" pode produzir um elétron "pular os trilhos" de um circuito para outro, sem precisar das condições adequadas para um arco elétrico tradicional); portanto, praticamente todos os fabricantes de chips estão focados em tornar cada relógio capaz de fazer mais, colocando mais "unidades de execução" em cada CPU. Então, em vez de o computador fazer apenas uma coisa por relógio, ele pode fazer 2, 4 ou até 8. A Intel possui "HyperThreading", que basicamente divide um núcleo da CPU em dois processadores lógicos (com algumas limitações). Praticamente todos os fabricantes estão colocando pelo menos dois núcleos de CPU separados em um chip de CPU, e o atual padrão ouro para CPUs de desktop é de quatro núcleos por chip. Oito é possível quando dois chips de CPU são usados, há placas-mãe de servidor projetadas para processadores "quad quad-core" (16 EUs mais HT opcional), e é provável que a próxima geração de CPUs tenha seis ou oito por chip.
O resultado disso é que, para tirar o máximo proveito da maneira como os computadores estão ganhando poder de computação, você deve permitir que o computador "divida e conquiste" seu programa. Os idiomas gerenciados possuem pelo menos um encadeamento de GC que lida com o gerenciamento de memória separadamente do seu programa. Alguns também possuem threads de "transição" que manipulam a interoperabilidade COM / OLE (tanto para proteger a "caixa de areia" gerenciada quanto para o desempenho). Além disso, você realmente precisa começar a pensar em como seu programa pode fazer várias coisas simultaneamente e arquitetar seu programa com recursos projetados para permitir que partes do programa sejam manipuladas de forma assíncrona. O Windows e os usuários do Windows praticamente esperam que seu programa execute tarefas longas e complicadas em threads de segundo plano, que mantém a interface do usuário do seu programa (que é executada no thread principal do programa) "responsiva" ao loop de mensagens do Windows. Obviamente, problemas que têm soluções paralelizáveis (como classificação) são candidatos naturais, mas há um número finito de tipos de problemas que se beneficiam da paralelização.
fonte
Apenas um aviso sobre multithreading: mais threads não significam melhor eficiência. Se não forem gerenciados corretamente, eles podem tornar o sistema mais lento. O ator da Scala aprimora o encadeamento do Java e maximiza o uso do sistema (mencionado como você é um desenvolvedor Java).
EDIT: Aqui estão algumas coisas que você deve ter em mente sobre as desvantagens do multithreading:
Além disso, esse link pode ser de alguma ajuda sobre o mesmo.
fonte
Em campos críticos de desempenho, nos quais o desempenho não é proveniente de códigos de terceiros que realizam trabalhos pesados, mas sim os nossos, então eu tenderia a considerar as coisas nesta ordem de importância da perspectiva da CPU (GPU é um curinga que ganhei entrar):
Observe que esta lista não se baseia apenas na importância, mas em muitas outras dinâmicas, como o impacto que elas causam na manutenção, o quanto elas são diretas (se não, vale a pena considerar com mais antecedência), suas interações com outras pessoas da lista etc.
Eficiência de memória
A maioria pode se surpreender com a minha escolha de eficiência de memória em vez de algorítmica. Isso ocorre porque a eficiência da memória interage com todos os outros 4 itens desta lista, e é porque a consideração está frequentemente na categoria "design" e não na categoria "implementação". É certo que existe um problema de galinha ou ovo aqui, pois entender a eficiência da memória geralmente requer considerar todos os 4 itens da lista, enquanto todos os outros 4 itens também exigem considerar a eficiência da memória. No entanto, está no coração de tudo.
Por exemplo, se precisarmos de uma estrutura de dados que ofereça acesso seqüencial em tempo linear e inserções em tempo constante na parte traseira e nada mais para pequenos elementos, a ingênua opção aqui a ser alcançada seria uma lista vinculada. Isso desconsidera a eficiência da memória. Quando consideramos a eficiência da memória no mix, acabamos escolhendo estruturas mais contíguas nesse cenário, como estruturas baseadas em matrizes crescíveis ou nós mais contíguos (por exemplo: um que armazena 128 elementos em um nó) vinculados ou, no mínimo, uma lista vinculada apoiada por um alocador de pool. Eles têm uma vantagem dramática, apesar de terem a mesma complexidade algorítmica. Da mesma forma, geralmente escolhemos a classificação rápida de uma matriz e a classificação de mesclagem, apesar de uma complexidade algorítmica inferior, simplesmente devido à eficiência da memória.
Da mesma forma, não podemos ter multithreading eficiente se nossos padrões de acesso à memória são tão granulares e dispersos por natureza que acabamos maximizando a quantidade de compartilhamento falso enquanto bloqueamos nos níveis mais granulares do código. Portanto, a eficiência da memória multiplica a multithreading de eficiência. É um pré-requisito para aproveitar ao máximo os threads.
Cada item acima na lista tem uma interação complexa com os dados, e o foco na forma como os dados são representados é, em última análise, o objetivo da eficiência da memória. Cada um dos itens acima pode ter gargalos com uma maneira inadequada de representar ou acessar dados.
Outra eficiência da memória razão é tão importante é que ele pode aplicar em toda a toda a base de código. Geralmente, quando as pessoas imaginam que as ineficiências se acumulam a partir de pequenas seções de trabalho aqui e ali, é um sinal de que elas precisam pegar um criador de perfil. No entanto, os campos de baixa latência ou aqueles que lidam com hardware muito limitado encontrarão, mesmo após a criação de perfil, sessões que não indicam pontos de acesso claros (apenas algumas vezes dispersas por todo o lugar) em uma base de código que é flagrantemente ineficiente na maneira como aloca, copia e acessando a memória. Normalmente, essa é a única vez que uma base de código inteira pode ser suscetível a uma preocupação de desempenho que pode levar a um novo conjunto de padrões aplicados em toda a base de código, e a eficiência da memória geralmente está no centro dela.
Algorítmico
Esse é praticamente um dado, pois a escolha em um algoritmo de classificação pode fazer a diferença entre uma entrada massiva que leva meses para classificar versus segundos para classificar. Causa o maior impacto de todos, se a escolha for entre, digamos, algoritmos quadráticos ou cúbicos realmente abaixo do par e um linearitmico, ou entre linear e logarítmico ou constante, pelo menos até termos mais de 1.000.000 de máquinas principais (nesse caso, memória eficiência se tornaria ainda mais importante).
No entanto, ele não está no topo da minha lista pessoal, já que alguém competente em seu campo saberia usar uma estrutura de aceleração para a seleção de frustum, por exemplo, estamos saturados de conhecimento algorítmico e saber coisas como usar uma variante de um trie como uma árvore de raiz para pesquisas baseadas em prefixos é coisa de bebê. Na falta desse tipo de conhecimento básico do campo em que estamos trabalhando, a eficiência algorítmica certamente chegaria ao topo, mas muitas vezes a eficiência algorítmica é trivial.
Também inventar novos algoritmos pode ser uma necessidade em alguns campos (por exemplo: no processamento de malha, tive que inventar centenas, pois eles não existiam antes ou as implementações de recursos semelhantes em outros produtos eram segredos de propriedade, não publicados em um artigo ) No entanto, uma vez que passamos pela parte da solução de problemas e encontramos uma maneira de obter os resultados corretos, e uma vez que a eficiência se torna o objetivo, a única maneira de realmente obtê-la é considerar como estamos interagindo com os dados (memória). Sem entender a eficiência da memória, o novo algoritmo pode se tornar desnecessariamente complexo com esforços fúteis para torná-lo mais rápido, quando a única coisa necessária era uma consideração um pouco mais da eficiência da memória para gerar um algoritmo mais simples e elegante.
Por fim, os algoritmos tendem a estar mais na categoria "implementação" do que na eficiência da memória. Em geral, é mais fácil melhorar em retrospectiva, mesmo com um algoritmo abaixo do ideal usado inicialmente. Por exemplo, um algoritmo inferior de processamento de imagem geralmente é implementado em um local local na base de código. Pode ser trocado por um melhor mais tarde. No entanto, se todos os algoritmos de processamento de imagem estiverem vinculados a uma
Pixel
interface que tenha uma representação de memória abaixo do ideal, mas a única maneira de corrigi-lo é alterar a maneira como vários pixels são representados (e não um único), então geralmente SOL e terá que reescrever completamente a base de código para umImage
interface. O mesmo tipo de coisa vale para substituir um algoritmo de classificação - geralmente é um detalhe de implementação, enquanto uma alteração completa na representação subjacente dos dados que estão sendo classificados ou na maneira como eles passam pelas mensagens pode exigir que as interfaces sejam redesenhadas.Multithreading
O multithreading é difícil no contexto do desempenho, pois é uma otimização em nível micro que atende às características do hardware, mas nosso hardware está realmente escalando nessa direção. Já tenho colegas que têm 32 núcleos (só tenho 4).
No entanto, o multithreading está entre as micro-otimizações mais perigosas, provavelmente conhecidas por um profissional, se o objetivo for usado para acelerar o software. A condição de corrida é praticamente o bug mais mortal possível, pois é de natureza tão indeterminista (talvez apenas aparecendo uma vez a cada poucos meses na máquina de um desenvolvedor no momento mais inconveniente fora do contexto de depuração, se houver). Portanto, tem sem dúvida a degradação mais negativa na capacidade de manutenção e potencial correção de código entre todas elas, especialmente porque os erros relacionados ao multithreading podem facilmente voar sob o radar, mesmo nos testes mais cuidadosos.
No entanto, está se tornando tão importante. Embora nem sempre possa superar algo como a eficiência da memória (que às vezes pode tornar as coisas cem vezes mais rápidas), dado o número de núcleos que temos agora, estamos vendo cada vez mais núcleos. É claro que, mesmo em máquinas com 100 núcleos, eu ainda colocaria a eficiência da memória no topo da lista, pois a eficiência do encadeamento é geralmente impossível sem ela. Um programa pode usar uma centena de threads em uma máquina assim e ainda ser lento, sem representação eficiente da memória e padrões de acesso (que se vincularão aos padrões de bloqueio).
SIMD
O SIMD também é um pouco estranho, pois os registros estão realmente ficando mais amplos, com planos de se tornar ainda mais amplos. Originalmente, vimos registros MMX de 64 bits seguidos por registros XMM de 128 bits, capazes de realizar 4 operações SPFP em paralelo. Agora estamos vendo registros YMM de 256 bits com capacidade para 8 em paralelo. E já existem planos para registros de 512 bits que permitiriam 16 em paralelo.
Eles interagem e se multiplicam com a eficiência do multithreading. No entanto, o SIMD pode degradar a capacidade de manutenção tanto quanto o multithreading. Embora os bugs relacionados a eles não sejam necessariamente tão difíceis de reproduzir e corrigir como uma condição de impasse ou corrida, a portabilidade é incômoda e garantir que o código possa ser executado na máquina de todos (e usar as instruções apropriadas com base em seus recursos de hardware) é estranho.
Outra coisa é que, embora os compiladores hoje em dia geralmente não batam o código SIMD escrito por especialistas, eles vencem tentativas ingênuas facilmente. Eles podem melhorar até o ponto em que não precisamos mais fazê-lo manualmente, ou pelo menos sem sermos tão manuais a ponto de escrever códigos intrínsecos ou de montagem direta (talvez apenas um pouco de orientação humana).
Novamente, porém, sem um layout de memória eficiente para o processamento vetorizado, o SIMD é inútil. Acabaremos apenas carregando um campo escalar em um registro amplo apenas para fazer uma operação nele. No centro de todos esses itens, há uma dependência dos layouts de memória para ser realmente eficiente.
Outras otimizações
Isso é frequentemente o que eu sugeriria que começássemos a chamar de "micro" hoje em dia se a palavra sugerir não apenas ir além do foco algorítmico, mas também em direção a mudanças que tenham um impacto minúsculo no desempenho.
Muitas vezes, tentar otimizar a previsão de ramificação requer uma alteração na eficiência do algoritmo ou da memória, por exemplo, se isso for tentado apenas através de dicas e reorganização do código para previsão estática, isso só tende a melhorar a execução inicial desse código, tornando os efeitos questionáveis se nem sempre é insignificante.
Voltar para Multithreading for Performance
Enfim, qual a importância da multithreading de um contexto de desempenho? Na minha máquina de 4 núcleos, o ideal é tornar as coisas cerca de 5 vezes mais rápidas (o que posso obter com o hyperthreading). Seria consideravelmente mais importante para o meu colega que tem 32 núcleos. E isso se tornará cada vez mais importante nos próximos anos.
Então é muito importante. Mas é inútil apenas lançar um monte de threads no problema se a eficiência da memória não existir para permitir que os bloqueios sejam usados com moderação, para reduzir o compartilhamento falso etc.
Multithreading fora do desempenho
Multithreading nem sempre é sobre puro desempenho, em um sentido direto da taxa de transferência. Às vezes, é usado para equilibrar uma carga, mesmo com o custo possível da taxa de transferência, para melhorar a capacidade de resposta ao usuário ou permitir que o usuário faça mais multitarefas sem esperar que as coisas terminem (por exemplo: continue navegando enquanto faz o download de um arquivo).
Nesses casos, eu sugiro que o multithreading suba ainda mais para o topo (possivelmente até acima da eficiência da memória), já que trata-se de design do usuário final, e não de tirar o máximo proveito do hardware. Muitas vezes, ele domina os designs de interface e a maneira como estruturamos toda a nossa base de código em tais cenários.
Quando não estamos simplesmente paralelizando um loop restrito acessando uma estrutura de dados massiva, o multithreading vai para a categoria "design" realmente incondicional, e o design sempre supera a implementação.
Portanto, nesses casos, eu diria que considerar o multithreading inicial é absolutamente crítico, ainda mais do que a representação e o acesso à memória.
fonte
Programação simultânea e paralela é o que está se tornando importante. Threads são apenas um modelo de programação para fazer várias coisas ao mesmo tempo (e não em pseudo-paralelo como costumava ser antes do surgimento de processadores com vários núcleos). O multithread é (IMHO razoavelmente) criticado por ser complexo e perigoso, pois os threads compartilham muitos recursos e o programador é responsável por fazê-los cooperar. Caso contrário, você acaba com conflitos que são difíceis de depurar.
fonte
Como podemos precisar entrar em contato com muitos aplicativos externos, pode haver algum processo em segundo plano onde a interação do sistema externo leva mais tempo e o usuário final não pode esperar até que o processo seja concluído. então Multithreading é importante ..
como estamos usando em nosso aplicativo, primeiro tentamos entrar em contato com o sistema externo, se estiver inoperante, em seguida, salvamos a solicitação no banco de dados e expandimos um segmento para concluir o processo em backgound. Pode ser necessário também nas operações em lote.
fonte
Historicamente, as pessoas tinham que lutar fazendo programação multithread manualmente. Eles tiveram que trabalhar com todos os componentes principais (threads, semáforos, mutexes, bloqueios, etc.) diretamente.
Todos esses esforços resultaram em aplicativos capazes de escalar adicionando cpus adicionais a um único sistema. Essa escalabilidade vertical é limitada pelo "qual é o maior servidor que posso comprar".
Atualmente vejo uma mudança no uso de mais estruturas e modelos de design diferentes para o design de software. O MapReduce é um desses modelos, focado no processamento em lote.
O objetivo é escalar horizontalmente. Adicionando mais servidores padrão em vez de comprar servidores maiores.
Dito isto, permanece o fato de que realmente entender a programação multithread é muito importante. Eu estive na situação em que alguém criou uma condição de corrida e nem sabia o que é uma condição de corrida até que notamos erros estranhos durante o teste.
fonte
Minha máquina possui 8 núcleos. No Gerenciador de tarefas, tenho 60 processos em execução. Alguns, como o VS, usam até 98 threads. O Outlook usa 26. Espero que a maior parte do meu uso de memória seja as pilhas alocadas a cada um desses threads ociosos.
Pessoalmente, estou aguardando a saída do computador de 300 núcleos para não precisar esperar que o Outlook responda. É claro que até então o Outlook usará 301 threads.
A multithreading só importa se você estiver construindo sistemas que serão o único processo importante no computador em um determinado momento (por exemplo, mecanismos de cálculo). Os aplicativos de desktop provavelmente fariam um favor ao usuário por não usar todos os núcleos disponíveis. Os aplicativos da Web que usam o modelo de solicitação / resposta são inerentemente multiencadeados.
É importante para designers de estrutura e linguagem e programadores de sistemas de back-end - não muito para os construtores de aplicativos. Provavelmente vale a pena entender alguns conceitos básicos, como bloquear e escrever código assíncrono.
fonte