La notion de concurrence est implémentée dans Arcane via la notion de tâche.
Cette notion de tâche permet l'exécution concurrente de plusieurs opérations via les threads.
Cette notion est complémentaire de la notion de décomposition de domaine utilisée par le Arcane::IParallelMng. Il est donc tout à fait possible de mélanger décomposition de domaine et les threads.
Pour utiliser les tâches, il faut inclure le fichier suivant :
Il existe deux mécanismes pour utiliser les tâches :
La première solution est la plus simple et doit être envisagée en priorité.
Par défaut, le support de la concurrence est désactivé. L'activation se fait avant le lancement du code, en spécifiant le nombre de tâches pouvant s'exécuter de manière concurrentes lors de la ligne de commande (se reporter à la page Lancement d'un calcul pour savoir comment faire cela).
Il est possible de savoir dans le code si la concurrence est active en appelant la méthode Arcane::TaskFactory::isActive().
Il n'est pas possible d'activer la concurrence pendant l'exécution.
Il existe deux formes de boucles parallèles. La première forme s'applique sur les boucles classiques, la seconde sur les groupes d'entités.
Le mécanisme de fonctionnement est similaire aux directives omp parallel for
de OpenMp.
La première forme est pour paralléliser la boucle séquentielle suivante :
La parallélisation se fait comme suit : il faut d'abord écrire une classe fonctor qui représente l'opération que l'on souhaite effectuée sur un interval d'itération. Ensuite, il faut utiliser l'opération arcaneParallelFor() en spécifiant ce fonctor en argument comme suit :
Cette syntaxe est un peu verbeuse. Si le compilateur supporte la norme C++11, il est possible d'utiliser les lambda function pour simplifier l'écriture :
Une spécialisation existe pour les groupes d'entités. Pour paralléliser une énumération sur un groupe comme le code suivant :
Il faut écrire comme cela :
De même, avec le support du C++11, on peut simplifier :
Pour les boucles Arcane::arcaneParallelFor() et Arcane::arcaneParallelForeach(), il est possible de passer en argument une instance de ParallelLoopOptions pour configurer la boucle parallèle. Par exemple, il est possible de spécifier la taille de l'intervalle pour découper la boucle :
La création d'un tâche se fait via la fabrique de tâche. Il faut spécifier en argument un fonctor de la même manière que les boucles parallèles :
Une fois la tâche créée, il est possible de la lancer et d'attendre sa terminaison via la méthode ITask::launchAndWait(). Pour des raisons de simplicité, la tâche n'est pas lancée tant que cette méthode n'a pas été appelée.
Il est possible de créer des sous-tâches à partir d'une première tâche via la méthode Arcane::TaskFactory::createChildTask(). L'utilisateur doit gérer le lancement et l'attente des sous-tâches. Par exemple :
L'exemple complet suivant montre l'implémentation du calcul d'une suite de Fibonacci via le mécanisme des tâches.