Richiamo della funzione di blocco con contenuto asincrono

4

Sono sicuro che questo è un modello di progettazione comune, ma mi sembra di avere un punto cieco.

Ho il requisito che la chiamata alla funzione da Application to Service stia bloccando, ma il servizio deve fare qualcosa di asincrono.

Bene, posso gestirlo, MA , ci possono essere più applicazioni e c'è solo un servizio.

Mentre il servizio dovrebbe bloccare l'applicazione, dovrebbe anche essere in grado di gestire più richieste parallele da diverse applicazioni.

Suppongo di dover generare un nuovo thread per ogni richiesta dell'applicazione, ma come posso quindi tornare dalla discussione all'applicazione alla fine?

Non mi dispiace inserire un assemblatore per memorizzare l'indirizzo di ritorno dell'applicazione & pop it dopo, ma è davvero necessario?

Devo fare qualcosa di speciale in C ++ per contrassegnare la funzione di servizio come ri-entrante?

Qualcuno può sollevare la benda?

[Aggiorna] Non c'è alcuna correlazione tra le applicazioni. Inoltre, solo il servizio potrebbe generare un thread, non le applicazioni. Quindi, sto pensando ad una service main che genera un gruppo di service thread s, ma non vedo come posso gestire la funzione di blocco delle chiamate & tornare.

    
posta Mawg 21.05.2015 - 13:58
fonte

3 risposte

4

Devi utilizzare un costrutto di blocco per ogni richiesta.

Questo è chiamato Futures (programmazione)

Ogni richiesta avrà il suo futuro. Una volta avviata l'elaborazione della richiesta (eventualmente in un pool separato di thread, come descritto), il chiamante verrà bloccato in caso di evasione o fallimento del futuro.

Quando arriva il risultato, il Futuro è sbloccato e la chiamata tornerà all'applicazione.

Quando usi questo modello, devi stare attento a prevenire deadlock e zombi. In altre parole, il Futuro deve essere sbloccato a un certo momento, indipendentemente dal fatto che il risultato sia riuscito o fallito (e in caso di fallimento, restituire un'eccezione all'applicazione è un gioco leale) - la chiamata non deve semplicemente bloccarsi indefinitamente.

Per quanto riguarda il design del pool di thread:

  • Ci sarà almeno un thread per ogni richiedente simultaneo (cioè un'applicazione), che verrà bloccato durante la richiesta.
    • Se le applicazioni e il servizio si trovano nello stesso processo, questi thread sono gli stessi dei thread dell'applicazione, che verranno bloccati.
  • Ci sarà un pool di thread separato, che richiede il numero di thread necessario per eseguire il lavoro tra il servizio e Internet. Questi thread si aggiungono ai thread di accettazione delle richieste.
    • Il numero esatto dipende da come è stato svolto il lavoro. È possibile utilizzare il modello asincrono (reattore) qui, che potrebbe ridurre il numero di thread necessari, ma ciò non avrà alcun effetto sul numero di thread di accettazione delle richieste.

Se la richiesta da Application to Service si verifica sulla rete, esiste un disaccoppiamento tra i thread di applicazione e i thread di servizio (in altre parole, un blocco di una richiesta di applicazione non implica affatto un "thread"; solo una non risposta da una connessione di rete). In tal caso, il Servizio può anche utilizzare il modello di reattore, il che significa che è possibile ridurre ulteriormente il numero di thread.

    
risposta data 21.05.2015 - 15:32
fonte
1

Poiché l'applicazione è bloccata mentre è in corso la chiamata di servizio, ogni applicazione deve già avere il suo thread se ci devono essere più chiamate di servizio contemporaneamente.

Supponendo che la chiamata di servizio avvia semplicemente l'operazione asincrona, forse fa qualcos'altro e poi blocca fino al termine dell'operazione, non vedo il problema. Dal momento che ogni chiamata al servizio è sul proprio thread, puoi semplicemente bloccare quel thread.

    
risposta data 21.05.2015 - 15:21
fonte
1

La risposta breve alla tua domanda è che non si ritorna dal codice asincrono. Invece si passa un oggetto di contesto al codice asincrono per inviare una risposta al termine dell'elaborazione della richiesta. Questo viene fatto principalmente in due forme:

Stai utilizzando un framework server che già si occupa di async per te:

void init() {
    registerHandler("requestType", [](ReqType req, Response resp) {
        // ..
        resp.send(data);
    });
}

O se gestisci richieste client in un ciclo di eventi di blocco, puoi ottenere asincrono usando multi-thread, come questo:

while (true) {
    // ...
    Req req = recv();
    thread t(processReq, req, ctx);
}

void processReq(Req req, Context &ctx) {
    // ...
    ctx.send(response);
}

Se è necessario eseguire più operazioni asincrone per soddisfare una richiesta, si utilizza il futuro per concatenare le operazioni asincrone, a condizione che le operazioni asincrone restituiscano un tipo futuro / promettente. Non è come aspettare l'operazione asincrona per tornare:

void processReq(Req req, Context &ctx) {
    // ...
    op1().then(op2).then(op3);
}

Un'ultima parola, se intendevi aspettare che il codice asincrono finisca prima di uscire dal processo, quindi usa il metodo join degli oggetti thread o usa il metodo wait dei tuoi oggetti promessa.

    
risposta data 07.02.2018 - 23:12
fonte