2014-07-27 15 views
7

Ho letto in alcune fonti che il comando stampa non è thread-safe e la soluzione alternativa è utilizzare il comando sys.stdout.write, ma ancora non funziona per me e la scrittura sul STDOUT non è atomico.Come posso scrivere atomicamente su stdout in python?

Ecco un breve esempio (chiamato questo file parallelExperiment.py):

import os 
    import sys 
    from multiprocessing import Pool 

    def output(msg): 
    msg = '%s%s' % (msg, os.linesep) 
    sys.stdout.write(msg) 

    def func(input): 
    output(u'pid:%d got input \"%s\"' % (os.getpid(), str(input))) 

    def executeFunctionInParallel(funcName, inputsList, maxParallelism): 
     output(u'Executing function %s on input of size %d with maximum parallelism of %d' % (
      funcName.__name__, len(inputsList), maxParallelism)) 
     parallelismPool = Pool(processes=maxParallelism) 
     executeBooleanResultsList = parallelismPool.map(funcName, inputsList) 
     parallelismPool.close() 
     output(u'Function %s executed on input of size %d with maximum parallelism of %d' % (
      funcName.__name__, len(inputsList), maxParallelism)) 
     # if all parallel executions executed well - the boolean results list should all be True 
     return all(executeBooleanResultsList) 

    if __name__ == "__main__": 
    inputsList=[str(i) for i in range(20)] 
    executeFunctionInParallel(func, inputsList, 4) 

osservare l'output:

i. Uscita di chiamare pitone parallelExperiment.py (fare attenzione che la parola "pid" è incasinato in alcune linee):

Executing function func on input of size 20 with maximum parallelism of 4 
ppid:2240 got input "0" 
id:4960 got input "2" 
pid:4716 got input "4" 
pid:4324 got input "6" 
ppid:2240 got input "1" 
id:4960 got input "3" 
pid:4716 got input "5" 
pid:4324 got input "7" 
ppid:4960 got input "8" 
id:2240 got input "10" 
pid:4716 got input "12" 
pid:4324 got input "14" 
ppid:4960 got input "9" 
id:2240 got input "11" 
pid:4716 got input "13" 
pid:4324 got input "15" 
ppid:4960 got input "16" 
id:2240 got input "18" 
ppid:2240 got input "19" 
id:4960 got input "17" 
Function func executed on input of size 20 with maximum parallelism of 4 

II. Uscita di chiamare pitone parallelExperiment.py> parallelExperiment.log, che significa reindirizzare il stdout al file parallelExperiment.log (attenzione che l'ordine delle linee non è buona, perché prima e dopo aver chiamato a executeFunctionInParallel che chiama Funz in parallelo, un messaggio deve essere stampato):

pid:3244 got input "4" 
pid:3244 got input "5" 
pid:3244 got input "12" 
pid:3244 got input "13" 
pid:240 got input "0" 
pid:240 got input "1" 
pid:240 got input "8" 
pid:240 got input "9" 
pid:240 got input "16" 
pid:240 got input "17" 
pid:1268 got input "2" 
pid:1268 got input "3" 
pid:1268 got input "10" 
pid:1268 got input "11" 
pid:1268 got input "18" 
pid:1268 got input "19" 
pid:3332 got input "6" 
pid:3332 got input "7" 
pid:3332 got input "14" 
pid:3332 got input "15" 
Executing function func on input of size 20 with maximum parallelism of 4 
Function func executed on input of size 20 with maximum parallelism of 4 
+0

Il risultato incasinato è ciò che è previsto per il multi-threading. Si generano thread che hanno tutti una propria timeline. Il fatto che bash rovini la stringa * pid * non è inusuale. È soprattutto, come hai detto, che in realtà non accetta più recettori multipli. Stampa solo ciò che ottiene e a volte mette in disordine callback sovrapposti. –

risposta

7

Questo avviene a causa delle multiprocessing.Pool utilizza effettivamente sottoprocessi invece di fili. È necessario utilizzare lo synchronization esplicito tra i processi. Nota, l'esempio sul link, risolve il tuo problema.

import os 
import sys 
from multiprocessing import Pool, Lock 

lock = Lock() 

def output(msg): 
    msg = '%s%s' % (msg, os.linesep) 
    with lock: 
     sys.stdout.write(msg) 

def func(input): 
    output(u'pid:%d got input \"%s\"' % (os.getpid(), str(input))) 

def executeFunctionInParallel(funcName, inputsList, maxParallelism): 
    output(u'Executing function %s on input of size %d with maximum parallelism of %d' % (
     funcName.__name__, len(inputsList), maxParallelism)) 
    parallelismPool = Pool(processes=maxParallelism) 
    executeBooleanResultsList = parallelismPool.map(funcName, inputsList) 
    parallelismPool.close() 
    output(u'Function %s executed on input of size %d with maximum parallelism of %d' % (
     funcName.__name__, len(inputsList), maxParallelism)) 
    # if all parallel executions executed well - the boolean results list should all be True 
    return all(executeBooleanResultsList) 

if __name__ == "__main__": 
    inputsList=[str(i) for i in range(20)] 
    executeFunctionInParallel(func, inputsList, 4) 
+0

e posso usarlo il metodo Pool.map? l'esempio è per l'utilizzo dell'oggetto "processo" –

+0

Ovviamente è possibile. Pool stesso utilizza internamente gli oggetti di processo. –

+0

Mi dispiace ma non sto seguendo .. cosa dovrei cambiare nei ** metodi executeFunctionInParallel ** e ** func **? –

1

Se si vuole evitare di blocco e sono felice di andare a un'interfaccia di livello inferiore, è possibile ottenere POSIX comportamento O_APPEND con os.open, os.write (se il vostro sistema supporta); e vedi Is file append atomic in UNIX?.

Problemi correlati