Le variabili globali possono essere utilizzate nei programmi asincroni?

3

Questa potrebbe essere una domanda stupida, mi dispiace.

Ho letto molto sugli inconvenienti delle variabili globali su questo sito. Sto finalmente cercando di aumentare la qualità del mio codice per un grande progetto che verrà esaminato da molti. Comprendo che l'utilizzo di vars globali rende il codice più difficile da eseguire il debug, poiché non si sa dove la variabile possa essere modificata (rendendo il codice spaghetti).

Tuttavia, ho un caso abbastanza specifico in cui non posso davvero pensare a un modo per evitare variabili globali. Ho un programma in esecuzione su un dispositivo incorporato (sotto FreeRTOS, tutto in C) in cui ho circa 20 funzioni ragionevolmente complesse, in cui ci sono forse 10 variabili globali passate in giro. Circa 3/4 del codice è in tempo reale, mentre il resto è asincrono, usando le variabili che le altre funzioni manipolano e inviando i loro contenuti a un server.

Ecco uno snippet di pseudocodice:

float important_var = 0;

void some_synchronous_function(){
   do_something();
   math.operation(important_var);
}
void do_something(){
   important_var+=5;
}

void async(){
   if(important_var > 5){
       //whatever
   }
}

main(){
    some_synchronous_function();
}

Questo è un modo ragionevole per farlo? (cower in fear)

    
posta 0xDBFB7 24.05.2016 - 15:17
fonte

3 risposte

3

Modificare una variabile su un thread e leggerlo su un altro è sempre problematico. La modifica su due thread è peggiore.

Puoi usare le variabili atomiche. Una variabile atomica, ad esempio, garantisce che i lettori ottengano important_var = 0, quindi important_var = 5.0, quindi important_var = 10.0 e così via. Se important_var non è atomico, leggerlo solo quando viene modificato in un altro thread potrebbe produrre qualsiasi risultato .

Il tuo sistema operativo può supportare operazioni atomiche. Il più potente in pratica dice "cambia la variabile x da a a b", che può fallire. Ad esempio invece di aumentare important_var per 5, si dovrebbe lasciare tmp = important_var, quindi "change important_var from tmp to tmp + 5". Se qualcosa cambia important_var in mezzo, l'operazione fallisce e tu prova di nuovo.

Oltre a ciò si usano mutex, code sequenziali o tutti i tipi di meccanismi che evitano di cambiare una variabile mentre vengono cambiati da un altro thread.

    
risposta data 24.05.2016 - 21:08
fonte
2

Non è tanto il fatto che i globals rendano il codice più difficile da eseguire il debug, anche se se si dispone di un programma molto grande e di uno stato globale che è molto lontano dal suo utilizzo, ciò può avere un effetto negativo.

Il problema con lo stato globale è che è globale, singolare, difficile da estendere.

Pensa a questo, hai alcune variabili che vuoi modificare nelle tue funzioni, così le hai messe da qualche parte dove tutte le funzioni possono condividerle. Immagina che in futuro desideri estendere il tuo programma in modo da poter gestire 2 sistemi affiancati. Hai solo 1 set di stato globale da condividere, quindi non puoi sistemarlo facilmente. Quindi potresti mettere tutto il tuo stato globale in una singola classe e condividerla. Quando si desidera estendere il programma, è ora possibile creare 2 istanze di tale classe, una per ciascun server in esecuzione, rendendo il problema quello di decidere quale oggetto utilizzare per un determinato server.

Ora hai il tuo stato globale in oggetti più localmente detenuti, puoi persino passare quegli oggetti ai server al momento della creazione, isolandoli ulteriormente (è un'iniezione di dipendenza). E ora hai un server che in effetti non usa affatto il globale senza molti cambiamenti nel modo in cui funziona il programma.

    
risposta data 25.05.2016 - 09:50
fonte
0

Considera questa alternativa, che limita l'ambito di important_var a un singolo file sorgente e incapsula tutto il comportamento importante in una classe in stile C.

important_class.c

static float important_var = 0.0;

void do_important_operation(void)
{
   math.operation(important_var);
}

void do_important_adjustment(float amount)
{
   important_var += amount;
}

bool does_important_condition_exist(void)
{
   return (important_var > 5.0);
}

important_class.h

void do_important_operation(void);
void do_important_adjustment(float amount);
bool does_important_condition_exist(void);

main.c

#include "important_class.h"

void some_synchronous_function(){
   do_something();
   do_important_operation();
}

void do_something(){
   do_important_adjustment(5.0);
}

void async(){
   if(does_important_condition_exist()){
       //whatever
   }
}

main(){
    some_synchronous_function();
}

Probabilmente la tua implementazione va bene per un'applicazione relativamente semplice a cui important_var non è accessibile da troppe posizioni. Ma questa implementazione in stile classe è più robusta ed estensibile e sarà più facile da mantenere man mano che si aggiungono nuove funzionalità e complessità.

Si noti che questa implementazione in stile classe deve ancora essere protetta da thread se più thread e / o ISR chiamano i metodi important_class .

    
risposta data 11.06.2016 - 18:21
fonte

Leggi altre domande sui tag