Pool di thread separati per I / O e CPU

6

Sono stato sconcertante su una buona implementazione per questo per un po '. Ho un programma che esegue un'operazione I / O di lunga durata (scaricando un file) e un'operazione CPU a esecuzione prolungata (analizzando il suo contenuto). Per migliorare l'efficienza, volevo che un pool di thread eseguisse la sezione I / O e facesse passare le attività a un altro pool di thread che esegue la sezione CPU. In questo modo, è improbabile che tutti i thread rimangano bloccati sull'intero I / O o sulla CPU. A seconda del file, un'operazione probabilmente richiederà più tempo dell'altra.

Qual è il modo migliore per eseguire un hand-off tra pool di thread? Supponendo che sto usando Java con due ExecutorServices, in questo modo:

int threads = Runtime.getRuntime().availableProcessors();
ExecutorService ioService = Executors.newFixedThreadPool(threads);
ExecutorService cpuService = Executors.newFixedThreadPool(threads);

public BigFile ioTask(){
    return Connection.downloadBigFile();
}
public void cpuTask(BigFile bigFile){
    processBigFile(bigFile);
}

Qual è il modo migliore per far eseguire un'attività in ioService per aggiungere un'attività a cpuService mentre si trasferiscono i dati in essa?

Quello che ho considerato:

  • Invia un Runnable a cpuService che esegue un Callable in ioService . Blocca cpuService durante l'esecuzione di Callable .
  • Invia un Runnable a ioService che esegue un Runnable in cpuService . Blocca ioService durante l'esecuzione del secondo Runnable .
  • Crea un'implementazione Runnable con un costruttore di IOTaskResult , consentendo a ioService runnables di inviare queste implementazioni a cpuService . Trasforma un oggetto di elaborazione riutilizzabile in un oggetto processo consumabile, che aggiunge un ulteriore sovraccarico.
  • Aggiungi un metodo per generare un Runnable al processore di task della CPU. Questo mi sembra una soluzione e un tipo di "hack-y", considerando che ho un metodo che posso chiamare e il Runnable semplicemente avvolge il metodo e compila i parametri per me.

C'è un buon modo per gestirlo? Mi piacerebbe sentire alcuni pensieri.

    
posta ndm13 14.04.2017 - 22:21
fonte

2 risposte

9

To improve efficiency, I wanted to have one thread pool perform the I/O section and have it pass tasks to another thread pool that performs the CPU section. That way, it's unlikely that all threads will be stuck either hogging the I/O or hogging the CPU.

In realtà non hai bisogno di due pool di thread per rimuoverlo, e farlo in questo modo può essere controproducente dai punti di vista di esecuzione e complessità.

In primo luogo, i thread sono economici e non dovresti esitare a usarne uno per ogni processo di download e processo se non ne stai eseguendo centinaia. La parte del processo di I / O dedicata passerà molto del suo tempo addormentato, e tu vorresti davvero essere in grado di prendere alcuni cicli di CPU quando arriva il frame successivo e poi tornare a dormire. Ho intenzione di sventolare il rallentamento che si verifica se molti file vengono scaricati contemporaneamente e torneranno in seguito.

In base al tuo attuale design, sembra che in questo caso, la tua definizione di efficienza significhi che i file scaricati prima vengano elaborati per primi. * Ciò significa che vuoi mettere un core su ogni processo di elaborazione e tenerlo lì finché non è finito . Puoi regolarlo utilizzando un semaforo che conta il numero di core nel sistema:

int num_cores = Runtime.getRuntime().availableProcessors();
core = new Semaphore(num_cores);

Ciò consentirà i primi thread num_cores che vogliono utilizzare un core per acquisirne uno, e tutti gli altri dovranno aspettare fino a quando uno non viene rilasciato. Con un modo per regolare il numero di thread utilizzando i core, l'intero processo può essere ridotto a una singola routine:

void downloadFileAndProcess(String url) {

    BigFile bigFile = Connection.downloadBigFile(url);

    core.acquire();  // Hold here for a core

    // TODO: Wrap in try/catch/finally for error resistance.
    processBigFile(bigFile);
    core.release();
}

Il numero di download simultanei può essere regolato con un secondo semaforo che viene acquisito e rilasciato intorno alla prima affermazione.

* Se questo non è il caso, non si guadagna efficienza limitando il numero di thread purché si mantenga il numero al numero di core. Se hai n cicli di lavoro di CPU da eseguire e c core, ci vorranno dei n / c cicli per un totale di volte non importa come lo dividi.

    
risposta data 15.04.2017 - 04:50
fonte
1

ioService non deve essere di availableCores() dimensioni. Le sue dimensioni dipendono dalle caratteristiche di rete di un computer e potrebbero anche essere illimitate.

Non bloccare o aspettare, questo fa perdere tempo. Invia un Runnable a ioService , scarica il file, pubblica un altro Runnable su cpuService e analizza il suo contenuto lì.

Puoi anche provare a usare l'IO asincrono di nio2 che non blocca i thread, quindi potresti finire usando solo cpuService . Puoi anche provare a utilizzare ForkJoinPool che utilizza le code bloccate.

    
risposta data 25.05.2018 - 17:28
fonte

Leggi altre domande sui tag