2009-11-05 11 views
3

Ok, je courais POV-Ray sur toutes les démos, mais POV est toujours mono-thread et n'utiliserait pas plus d'un core. J'ai donc commencé à penser à une solution dans BASH.Programmation BASH multi-threadée - méthode généralisée?

J'ai écrit une fonction générale qui prend une liste de commandes et les exécute dans le nombre désigné de sous-shells. Cela fonctionne en fait, mais je n'aime pas la façon dont il gère l'accès à la commande suivante de manière thread-safe multi-processus:

  • Il faut, comme un argument, un fichier avec les commandes (1 par ligne),
  • Pour obtenir la commande "next", chaque processus ("thread") sera:
    • Waits jusqu'à ce qu'il puisse créer un fichier de verrouillage, avec: en $ CMDFILE $ LockFile
    • Lisez la commande pour m le fichier,
    • Modifie $ CMDFILE en supprimant la première ligne,
    • Supprime le $ LOCKFILE.

Est-il possible de faire plus propre cela? Je ne pouvais pas obtenir les sous-shells pour lire une seule ligne d'une FIFO correctement. Par ailleurs, le but de ceci est d'améliorer ce que je peux faire sur une ligne de commande BASH, et de ne pas trouver de solutions non-bash. J'ai tendance à effectuer beaucoup de tâches compliquées à partir de la ligne de commande et je veux un autre outil dans la boîte à outils.

En attendant, voici la fonction qui gère l'obtention de la ligne suivante à partir du fichier. Comme vous pouvez le voir, il modifie un fichier sur disque chaque fois qu'il lit/supprime une ligne. C'est ce qui semble hackish, mais je ne trouve rien de mieux, puisque FIFO n'a pas fonctionné sans setvbuf() dans bash.

# 
# Get/remove the first line from FILE, using LOCK as a semaphore (with 
# short sleep for collisions). Returns the text on standard output, 
# returns zero on success, non-zero when file is empty. 
# 
parallel__nextLine() 
{ 
    local line rest file=$1 lock=$2 

    # Wait for lock... 
    until ln "${file}" "${lock}" 2>/dev/null 
    do sleep 1 
    [ -s "${file}" ] || return $? 
    done 

    # Open, read one "line" save "rest" back to the file: 
    exec 3<"$file" 
    read line <&3 ; rest=$(cat<&3) 
    exec 3<&- 

    # After last line, make sure file is empty: 
    ([ -z "$rest" ] || echo "$rest") > "${file}" 

    # Remove lock and 'return' the line read: 
    rm -f "${lock}" 
    [ -n "$line" ] && echo "$line" 
} 
+0

Postez votre code! –

+0

Jetez un oeil à 'lockfile-progs (1)' pour voir si cela aide. Le problème avec le FIFO peut avoir à voir avec la mise en mémoire tampon. Certains utilitaires ont un mode sans tampon. –

Répondre

7
#adjust these as required 
args_per_proc=1 #1 is fine for long running tasks 
procs_in_parallel=4 

xargs -n$args_per_proc -P$procs_in_parallel povray < list 

Notez la commande nproc venir bientôt coreutils sera automatiquement déterminer le nombre d'unités de traitement qui peuvent ensuite être transmis à -P

+0

Ma question concerne vraiment la programmation en bash, pas seulement l'exécution des tâches en parallèle. Mais c'est une bonne information - merci! – NVRAM

+0

BTW, ** grep -c^processeur/proc/cpuinfo ** semble fonctionner correctement sur Linux si **/proc ** est monté, bien que je n'ai pas beaucoup de machines sur lesquelles le tester. – NVRAM

+0

Oui, cela affichera le nombre de processeurs en ligne dans le système. Le nombre disponible pour un processus peut être plus petit en raison d'un jeu de tâches précédent par exemple. – pixelbeat

2

Si vous avez besoin d'une réelle sécurité de thread, je vous recommande de migrer vers un meilleur système de script. Avec python, par exemple, vous pouvez créer des vrais threads avec une synchronisation sécurisée en utilisant des sémaphores/files d'attente.

0

Je crois que vous êtes en train de forker des processus ici, et pas de threading. Je recommanderais de rechercher le support de thread dans un langage de script différent comme perl, python ou ruby.

+0

En effet, cela génère des sous-coquilles. Je pensais qu'il était clair dans le texte que je voulais dire le terme «fil» conceptuellement; J'ai fait quelques modifications pour le rendre plus clair. – NVRAM

+1

Le forcing de processus est assez bon marché dans les systèmes basés sur Unix, et les processus séparés sont souvent appelés "threads", de manière inexacte. Dans tous les cas, les processus séparés sont souvent meilleurs que les threads, car ils sont plus autonomes. –

+0

Je suis d'accord que les processus fourchus peuvent en fait être la meilleure approche que les threads. – dlamblin

2

désolé de cogner ce après si longtemps, mais je reconstitué une assez bonne solution pour cet OMI
Cela ne fonctionne pas parfaitement, mais cela limitera le script à un certain nombre de tâches enfants en cours d'exécution, puis attendra tout le reste à la fin.

#!/bin/bash 

pids=() 
thread() { 
    local this 
    while [ ${#} -gt 6 ]; do 
    this=${1} 
    wait "$this" 
    shift 
    done 
    pids=($1 $2 $3 $4 $5 $6) 
} 
for i in 1 2 3 4 5 6 7 8 9 10 
do 
    sleep 5 & 
    pids=(${pids[@]-} $(echo $!)) 
    thread ${pids[@]} 
done 
for pid in ${pids[@]} 
do 
    wait "$pid" 
done 

il semble fonctionner très bien pour ce que je fais (manipulation parallèle d'un ajout tas de fichiers à la fois) et il empêche de casser mon serveur, tout en assurant que tous les fichiers sont téléchargés avant qu'il se termine le script