Usando il modello attore, come si può programmare una parte concomitante (sezione critica) del codice come pepite autonome?

3

L'architettura Von Neumann consente l'elaborazione sequenziale delle istruzioni. Quindi, un singolo core all'interno di una CPU esegue le istruzioni in sequenza.

Considera che il sistema operativo fornisce il modello di threading 1-1 ( qui ) in un sistema con processore multi-core,

Properties of concurrent system

  • Multiple actors(say, each thread assigned to a different core)
  • Shared resource(heaps, global variables, devices)
  • Rules for access(Atomic/Conditional synchronization)

With atomic synchronization mechanism(Lock) or conditional synchronization mechanism(say Semaphore), messages are indirectly passed between actors, that help compliance with rules for accessing shared resource.

In questa risposta , si dice, Il modello attore aiuta a forzare a programmare porzioni concorrenti del tuo codice come nugget autonomi che possono essere eseguiti in parallelo e senza dipendere da un altro pezzo di codice.

Domanda:

Per aiutare a capire la differenza tra modello concorrente vs attore,

Usando il modello degli attori, come può un programma una parte concomitante (sezione critica) del codice come nugget autonomi?

    
posta overexchange 18.01.2017 - 02:31
fonte

2 risposte

4

Probabilmente è meglio pensare al modello dell'attore come antenato della programmazione orientata agli oggetti; e confrontalo con OOP. Tuttavia, poiché (spero) tu abbia già familiarità con OOP, farò il contrario.

Per OOP, immagina di avere un oggetto semplice con codice vagamente simile a questo:

integer currentValue = 0;

void incrementMethod(void) {
    currentValue = currentValue + 1;
}

void decrementMethod(void) {
    currentValue = currentValue - 1;
}

integer getValueMethod(void) {
    return currentValue;
}

void setValueMethod(integer value) {
    currentValue = value;
}

Questo ha problemi con la concorrenza, perché più thread / CPU possono eseguire i metodi contemporaneamente e rovinarsi l'un l'altro. La soluzione del "modello attore" consiste nel prevenire l'accesso diretto ai metodi e fornire l'isolamento tra "cose" (oggetti / attori). La conseguenza è che hai bisogno di una qualche forma di comunicazione tra le cose isolate. Quella comunicazione è il passaggio dei messaggi.

L'esempio stupido di cui sopra potrebbe diventare:

integer currentValue = 0;

void main(void) {
    while(running) {
        message = getMessage();
        switch(messsage->type) {
            case INCREMENT_REQUEST:
                incrementMethod();
                break;
            case DECREMENT_REQUEST:
                decrementMethod();
                break;
            case GET_VALUE_REQUEST:
                getValueMethod(message->senderID);
                break;
            case SET_VALUE_REQUEST:
                setValueMethod();
                break;
            default:
                sendMessage(senderID, UNKNOWN_REQUEST, NULL);
        }
    }
}

void incrementMethod(void) {
    currentValue = currentValue + 1;
}

void decrementMethod(void) {
    currentValue = currentValue - 1;
}

void getValueMethod(messagePort senderID) {
    sendMessage(senderID, GETVALUE_REPLY, currentValue);
}

void setValueMethod(integer value) {
    currentValue = value;
}

Poiché la messaggistica serializza le richieste e poiché nessun dato è condiviso tra thread, non è necessaria alcuna altra forma di controllo della concorrenza. Poiché nessun dato è condiviso tra thread, è anche altamente distribuibile (ad es. Puoi avere un sistema in cui diversi attori si trovano su computer fisici diversi). A causa dell'isolamento tra attori è "più possibile" (almeno in teoria) supportare cose come l'aggiornamento live (ad esempio sostituendo il codice di un attore mentre il resto del sistema è in esecuzione) e la tolleranza di errore (ad es. Tripla ridondanza con 3 attori su 3 computer diversi invece di uno). Infine, poiché un attore è in grado di accettare una richiesta sconosciuta, è più estensibile (ad esempio potresti inviare un messaggio di ADD_VALUE_REQUEST a un attore che non capisce, e poi fare in modo che l'attore capisca quel tipo di messaggio in seguito).

Il problema principale con (l'implementazione ingenua di) il modello di attore è la prestazione - con un thread per attore più il sovraccarico del messaggio che passa la performance (rispetto a OOP) sarebbe estremamente negativo. Esistono diversi modi per risolverlo, ma i metodi più comuni sono l'uso di molti attori per thread e per consentire agli attori all'interno della stessa discussione di chiamare direttamente i rispettivi metodi (ed evitare che il messaggio passi in quel caso). Con questo in mente, è possibile definire le origini di OOP come "modello attore con un solo thread in cui tutti gli attori possono chiamare direttamente i rispettivi metodi" (realizzando che il multi-threading è stato riadattato su OOP in seguito).

L'altra cosa che devo sottolineare è che per il mio esempio sono intenzionalmente "di basso livello" per aiutarti a capire cosa sta succedendo dietro le quinte. Per un linguaggio progettato per il modello attore, il ciclo di gestione dei messaggi mostrato sopra ( main() nella sua interezza) sarebbe implicito e non esplicito; per salvare il programmatore dal digitare così tante cose, e anche in modo che il compilatore possa fare le ottimizzazioni che ho citato.

    
risposta data 18.01.2017 - 09:07
fonte
2

Elimina la sezione critica

Dividere il lavoro in singole parti in modo tale da non richiedere più l'accesso simultaneo a un pool di memoria condiviso. Questo è inevitabile, dal momento che gli attori possono e spesso eseguono nel loro stesso processo.

Un modo per farlo è riscrivere il codice in modo che sia funzionalmente puro, piuttosto che un codice mutevole orientato agli oggetti. La programmazione funzionale, per sua stessa natura, richiede trasparenza referenziale. Le funzioni referential-transparent possono essere facilmente parallelizzate, perché non esiste una sezione critica.

Se i tuoi attori devono ancora condividere uno spazio di stato, usa un database con le funzionalità ACID . Si prenderà cura dei problemi di concorrenza e di blocco per te.

    
risposta data 18.01.2017 - 02:54
fonte