Questões relevantes :
Sobre o C ++ 11:
- C ++ 11: std :: thread em pool?
- O async (launch :: async) no C ++ 11 tornará obsoletos os pools de threads para evitar a criação cara de threads?
Sobre o Boost:
Como obtenho um pool de threads para enviar tarefas para , sem criar e excluí-las repetidamente? Isso significa threads persistentes para ressincronizar sem ingressar.
Eu tenho um código que se parece com isso:
namespace {
std::vector<std::thread> workers;
int total = 4;
int arr[4] = {0};
void each_thread_does(int i) {
arr[i] += 2;
}
}
int main(int argc, char *argv[]) {
for (int i = 0; i < 8; ++i) { // for 8 iterations,
for (int j = 0; j < 4; ++j) {
workers.push_back(std::thread(each_thread_does, j));
}
for (std::thread &t: workers) {
if (t.joinable()) {
t.join();
}
}
arr[4] = std::min_element(arr, arr+4);
}
return 0;
}
Em vez de criar e unir threads a cada iteração, prefiro enviar tarefas aos threads de trabalho a cada iteração e criá-las apenas uma vez.
c++
multithreading
c++11
threadpool
stdthread
Yktula
fonte
fonte
Respostas:
Você pode usar a biblioteca de conjuntos de threads C ++, https://github.com/vit-vit/ctpl .
Em seguida, o código que você escreveu pode ser substituído pelo seguinte
Você obterá o número desejado de threads e não os criará e excluirá repetidamente nas iterações.
fonte
results[j] = p.push([&arr, j](int){ arr[j] +=2; });
Isso é copiado da minha resposta para outro post muito semelhante, espero que possa ajudar:
1) Comece com o número máximo de threads que um sistema pode suportar:
2) Para uma implementação eficiente do conjunto de encadeamentos, uma vez que os encadeamentos são criados de acordo com o Num_Threads, é melhor não criar novos ou destruir os antigos (ingressando). Haverá uma penalidade de desempenho, pode até tornar seu aplicativo mais lento que a versão serial.
Cada encadeamento C ++ 11 deve estar executando em sua função com um loop infinito, aguardando constantemente novas tarefas serem executadas.
Aqui está como anexar essa função ao pool de threads:
3) A função Infinite_loop
Este é um loop "while (true)" aguardando a fila de tarefas
4) Crie uma função para adicionar trabalho à sua fila
5) Vincule uma função arbitrária à sua fila
Depois de integrar esses ingredientes, você terá seu próprio pool de segmentação dinâmico. Esses encadeamentos sempre são executados, aguardando a execução do trabalho.
Peço desculpas se houver algum erro de sintaxe, digitei esse código e tenho uma memória ruim. Lamento não poder fornecer o código completo do conjunto de encadeamentos, o que violaria a integridade do meu trabalho.
Edit: para finalizar o pool, chame o método shutdown ():
fonte
std::vector
não exige que seus elementos sejam copiáveis. Você pode usar vetores com tipos somente Move (unique_ptr
,thread
,future
, etc.).condition.wait
também deve procurar uma variávelstop_
e verificarif (stop_ == true) { break;}
?Um conjunto de encadeamentos significa que todos os encadeamentos estão em execução o tempo todo - em outras palavras, a função de encadeamento nunca retorna. Para dar aos threads algo significativo a ser feito, é necessário projetar um sistema de comunicação entre threads, com o objetivo de informar ao thread que há algo a ser feito, bem como para comunicar os dados de trabalho reais.
Normalmente, isso envolverá algum tipo de estrutura de dados simultânea, e cada encadeamento presumivelmente dormirá em algum tipo de variável de condição, que será notificada quando houver trabalho a ser feito. Ao receber a notificação, um ou vários threads são ativados, recuperam uma tarefa da estrutura de dados simultânea, processam-na e armazenam o resultado de maneira análoga.
O tópico continuaria verificando se ainda há mais trabalho a ser feito e, se não voltar a dormir.
O resultado é que você mesmo deve projetar tudo isso, pois não existe uma noção natural de "trabalho" que seja universalmente aplicável. É um pouco de trabalho, e há alguns problemas sutis que você precisa corrigir. (Você pode programar no Go se quiser um sistema que cuide do gerenciamento de threads nos bastidores.)
fonte
Um conjunto de encadeamentos é, no núcleo, um conjunto de encadeamentos, todos vinculados a uma função que funciona como um loop de eventos. Esses encadeamentos aguardam incessantemente a execução de uma tarefa ou seu próprio término.
O trabalho de conjunto de encadeamentos é fornecer uma interface para enviar trabalhos, definir (e talvez modificar) a política de execução desses trabalhos (regras de planejamento, instanciação de encadeamento, tamanho do conjunto) e monitorar o status dos encadeamentos e recursos relacionados.
Portanto, para um pool versátil, é preciso começar definindo o que é uma tarefa, como é iniciada, interrompida, qual é o resultado (veja a noção de promessa e futuro para essa pergunta), que tipo de eventos os threads terão que responder como eles irão lidar com eles, como esses eventos serão discriminados daqueles tratados pelas tarefas. Isso pode se tornar bastante complicado, como você pode ver, e impor restrições sobre como os encadeamentos funcionarão, pois a solução se torna cada vez mais envolvida.
As ferramentas atuais para lidar com eventos são bastante barebones (*): primitivas como mutexes, variáveis de condição e algumas abstrações além disso (bloqueios, barreiras). Mas, em alguns casos, essas abstrações podem se tornar impróprias (veja esta questão relacionada ), e é preciso voltar a usar as primitivas.
Outros problemas também precisam ser gerenciados:
Como isso aconteceria em seu ambiente?
Esta resposta a uma pergunta semelhante aponta para uma implementação existente destinada ao boost e ao stl.
Ofereci uma implementação muito grosseira de um conjunto de threads para outra pergunta, que não trata de muitos problemas descritos acima. Você pode desenvolver isso. Você também pode querer dar uma olhada nas estruturas existentes em outros idiomas, para encontrar inspiração.
(*) Não vejo isso como um problema, muito pelo contrário. Eu acho que é o próprio espírito de C ++ herdado de C.
fonte
fonte
Algo assim pode ajudar (extraído de um aplicativo que funciona).
Você pode usá-lo assim:
Lembre-se de que reinventar um mecanismo eficiente de enfileiramento assíncrono não é trivial.
O Boost :: asio :: io_service é uma implementação muito eficiente ou, na verdade, é uma coleção de wrappers específicos da plataforma (por exemplo, envolve as portas de conclusão de E / S no Windows).
fonte
std::thread
basta?std
paraboost::thread_group
.boost::thread_group
é uma coleção deboost::thread
instâncias. Mas é claro, é muito fácil substituirboost::thread_group
por umvector
destd::thread
s.Edit: Isso agora requer C ++ 17 e conceitos. (Em 9/12/16, apenas g ++ 6.0+ é suficiente.)
A dedução de modelo é muito mais precisa por causa disso, portanto, vale a pena o esforço de obter um compilador mais novo. Ainda não encontrei uma função que exija argumentos explícitos de modelo.
Agora, também é necessário qualquer objeto que possa ser chamado de maneira apropriada ( e ainda é estaticamente seguro! ).
Agora também inclui um pool de encadeamentos de prioridade de segmentação verde opcional usando a mesma API. Esta classe é apenas POSIX, no entanto. Ele usa a
ucontext_t
API para alternar tarefas do espaço do usuário.Eu criei uma biblioteca simples para isso. Um exemplo de uso é dado abaixo. (Estou respondendo isso porque foi uma das coisas que encontrei antes de decidir que era necessário escrevê-lo.)
Você pode passar
async
qualquer função com qualquer valor de retorno (ou nulo) e qualquer (ou nenhum) argumento e ele retornará um correspondentestd::future
. Para obter o resultado (ou apenas espere até que uma tarefa seja concluída), você chamaget()
o futuro.Aqui está o github: https://github.com/Tyler-Hardin/thread_pool .
fonte
Esta é outra implementação de conjunto de encadeamentos muito simples, fácil de entender e usar, que usa apenas a biblioteca padrão C ++ 11 e pode ser vista ou modificada para seus usos. Deve ser um bom começo para quem deseja usar o encadeamento piscinas:
https://github.com/progschj/ThreadPool
fonte
Você pode usar o thread_pool da biblioteca boost:
Você também pode usar o pool de threads da comunidade de código aberto:
fonte
Um pool de threads sem dependências fora do STL é totalmente possível. Recentemente, escrevi uma pequena biblioteca de encadeamentos de cabeçalho somente para resolver exatamente o mesmo problema. Ele suporta redimensionamento de pool dinâmico (alterando o número de trabalhadores em tempo de execução), aguardando, parando, pausando, retomando e assim por diante. Espero que você ache útil.
fonte