2013-01-20 6 views
2

J'ai une fonction qui est généralement exécuté 50 fois (pour exécuter 50 simulations). Habituellement, cela se fait séquentiellement à un seul thread mais je voudrais accélérer les choses en utilisant plusieurs threads. Les threads n'ont pas besoin d'accéder à la mémoire ou aux données des uns et des autres, donc je ne pense pas que la course soit un problème. Essentiellement, le thread devrait simplement terminer sa tâche, et revenir à l'essentiel c'est fini, retournant également une double valeur. Tout d'abord, en parcourant toute la documentation et les exemples de boost, je me suis vraiment compliqué et je ne sais plus trop ce que je cherche. boost :: thread? stimuler l'avenir? Quelqu'un pourrait-il donner un exemple de ce qui est applicable dans mon cas. En outre, je ne comprends pas comment spécifier le nombre de threads à exécuter, est-ce plus comme si j'exécutais 50 threads et que le système d'exploitation gère quand les exécuter?Boost Threading Conceptualisation/Questions

+1

Il semble comme un bon travail pour [ 'std :: async' ] (http://en.cppreference.com/w/cpp/thread/async) et ['std :: future'] (http://fr.cppreference.com/w/cpp/thread/future) (ou leurs variantes Boost). –

Répondre

1

Lisez les docs Boost.Thread Futures pour avoir une idée de l'utilisation de futures et async pour y parvenir. Il montre également comment le faire manuellement (à la dure) en utilisant thread objets.

Compte tenu de ce code de série:

double run_sim(Data*); 

int main() 
{ 
    const unsigned ntasks = 50; 

    double results[ntasks]; 
    Data data[ntasks]; 

    for (unsigned i=0; i<ntasks; ++i) 
    results[i] = run_sim(data[i]); 
} 

Une version parallèle naïve serait:

#define BOOST_THREAD_PROVIDES_FUTURE 
#include <boost/thread/future.hpp> 
#include <boost/bind.hpp> 

double run_task(Data*); 

int main() 
{ 
    const unsigned nsim = 50; 

    Data data[nsim]; 
    boost::future<int> futures[nsim]; 

    for (unsigned i=0; i<nsim; ++i) 
    futures[i] = boost::async(boost::bind(&run_sim, &data[i])); 

    double results[nsim]; 
    for (unsigned i=0; i<nsim; ++i) 
    results[i] = futures[i].get(); 
} 

Parce que boost::async ne supporte pas encore les fonctions différées chaque appel asynchrone va créer un nouveau thread, cette fera apparaître 50 fils à la fois. Cela pourrait faire assez mal, vous pouvez donc le diviser en petits blocs:

#define BOOST_THREAD_PROVIDES_FUTURE 
#include <boost/thread/future.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/bind.hpp> 

double run_sim(Data*); 

int main() 
{ 
    const unsigned nsim = 50; 
    unsigned nprocs = boost::thread::hardware_concurrency(); 
    if (nprocs == 0) 
    nprocs = 2; // cannot determine number of cores, let's say 2 

    Data data[nsim]; 
    boost::future<int> futures[nsim]; 
    double results[nsim]; 

    for (unsigned i=0; i<nsim; ++i) 
    { 
    if (((i+1) % nprocs) != 0) 
     futures[i] = boost::async(boost::bind(&run_sim, &data[i])); 
    else 
     results[i] = run_sim(&data[i]); 
    } 

    for (unsigned i=0; i<nsim; ++i) 
    if (((i+1) % nprocs) != 0) 
     results[i] = futures[i].get(); 
} 

Si hardware_concurrency() renvoie 4, cela va créer trois nouveaux fils alors appeler run_sim de manière synchrone dans le fil main(), puis créer un autre trois nouveaux fils alors appelez le run_sim de manière synchrone. Cela évitera que 50 threads soient tous créés en même temps, car le thread principal s'arrête pour effectuer une partie du travail, ce qui permettra à d'autres threads de se terminer.

Le code ci-dessus nécessite une version tout à fait récente d'utiliser Boost, il est un peu plus facile standard C++ si vous pouvez utiliser C++ 11:

#include <future> 

double run_sim(Data*); 

int main() 
{ 
    const unsigned nsim = 50; 
    Data data[nsim]; 

    std::future<int> futures[nsim]; 
    double results[nsim]; 

    unsigned nprocs = std::thread::hardware_concurrency(); 
    if (nprocs == 0) 
    nprocs = 2; 

    for (unsigned i=0; i<nsim; ++i) 
    { 
    if (((i+1) % nprocs) != 0) 
     futures[i] = std::async(boost::launch::async, &run_sim, &data[i]); 
    else 
     results[i] = run_sim(&data[i]); 
    } 

    for (unsigned i=0; i<nsim; ++i) 
    if (((i+1) % nprocs) != 0) 
     results[i] = futures[i].get(); 
} 
4

Si votre code est entièrement lié au processeur (pas d'E/S réseau/disque), il est recommandé de démarrer autant de threads d'arrière-plan que de CPU. Utilisez la fonction hardware_concurrency() de Boost pour déterminer ce nombre et/ou autoriser l'utilisateur à le définir. Commencer juste un tas de threads n'est pas utile, car cela augmentera la surcharge causée par la création, la commutation et l'arrêt des threads.

Le code commençant les threads est une simple boucle, suivie d'une autre boucle pour attendre la fin du thread. Vous pouvez également utiliser la classe thread_group pour cela. Si le nombre de tâches n'est pas connu et ne peut pas être distribué au démarrage du thread, envisagez d'utiliser un pool de threads dans lequel vous commencez juste un nombre raisonnable de threads, puis leur donnez des jobs pendant qu'ils apparaissent.