Soluzione semplice per chiamare una funzione solo su un thread, in coda per le chiamate in attesa?

3

Supponiamo che esista una funzione EventHandler chiamata su più thread in momenti diversi.

EventHandler deve chiamare SomeOtherFunction , ma queste chiamate devono avvenire solo su un thread alla volta.

Ciò significa: se una chiamata a SomeOtherFunction è attualmente attiva sul thread A e EventHandler viene richiamata di nuovo (su qualche altro thread B), quindi SomeOtherFunction non deve essere richiamato sul thread B, ma SomeOtherFunction deve essere chiamato quando ritorna sul thread A.

Qualcosa di simile a questo (pseudo-codice):

function EventHandler()
{
  numberPendingCallsSemaphore.Release();    // increase
  if (mayCallSemaphore.Acquire())           // (i)
  {
    while (numberPendingCallsSemaphore.Acquire())  // decrease
    {
      SomeOtherFunction();
    }
    // (ii)
    mayCallSemaphore.Release();
  }
}

Ovviamente, questo codice non funziona a causa di una gara: un thread è in posizione (ii) con mayCallSemaphore ancora preso e un altro in posizione (i) non è in grado di prendi mayCallSemaphore .

Ho scritto codice funzionante usando un lucchetto, ma il codice è piuttosto lungo e brutto. Dall'altro, questo problema sembra piuttosto semplice a prima vista, quindi:

C'è una soluzione semplice e potenzialmente semplice per questo che non ero in grado di trovare o che al momento non vedo?

    
posta Martin 27.02.2018 - 15:20
fonte

1 risposta

3

La tua soluzione proposta ha due oggetti di controllo. mayCallSemaphore sembra un Mutex in base a come lo stai usando e numberPendingCallsSemaphore sembra essere una variabile di condizioni . L'hai quasi corretto, ma come hai ipotizzato, avere due strutture di controllo che operano in modo indipendente porta a condizioni di gara, deadlock o entrambe a seconda della topologia e della situazione. Sono anche possibili wakeup perduti e altre cose.

Se osservi attentamente questa documentazione GitHub su come costruire un semaforo di conteggio usando sia un Mutex che una variabile di condizione , vedrai che il CV usa effettivamente (collabora con) il Mutex internamente, quindi non ci possono essere razze o deadlock. Copia e incolla la soluzione di esempio qui per comodità:

typedef struct sem_t {
  int count; 
  pthread_mutex_t m;
  pthread_condition_t cv;
} sem_t;

int sem_init(sem_t *s, int pshared, int value) {
  if (pshared) { errno = ENOSYS /* 'Not implemented'*/; return -1;}

  s->count = value;
  pthread_mutex_init(&s->m, NULL);
  pthread_cond_init(&s->cv, NULL);
  return 0;
}

sem_post(sem_t *s) {
  pthread_mutex_lock(&s->m);
  s->count++;
  pthread_cond_signal(&s->cv); /* See note */
  /* A woken thread must acquire the lock, so it will also have to wait until we call unlock*/

  pthread_mutex_unlock(&s->m);
}

sem_wait(sem_t *s) {
  pthread_mutex_lock(&s->m);
  while (s->count == 0) {
      pthread_cond_wait(&s->cv, &s->m); /*unlock mutex, wait, relock mutex*/
  }
  s->count--;
  pthread_mutex_unlock(&s->m);
}

Proteggi semplicemente la tua chiamata su SomeOtherFunction() chiamando sem_wait() prima e poi chiamando sem_post() dopo. I componenti interni dei metodi si prenderanno cura di tutti i dettagli, causando l'accesso simultaneo al blocco, consentendo solo un'esecuzione della sezione critica del codice alla volta. Questo è tutto basato sulle primitive Posix che fanno parte del sistema operativo e non possono andare male. Nel tuo caso, il conteggio dovrebbe essere limitato a uno, quindi puoi semplificare questo codice o inizializzare il semaforo con 1 e utilizzare lo stesso codice per un'altra situazione in seguito.

Ho utilizzato questo stesso approccio, preso dalle pagine del manuale POSIX (circa 20 anni fa), per creare un server di transazioni di database multi-thread e multi-processo prima che il server Web Apache fosse multi-thread. Ho avuto un thread principale che accetta connessioni TCP in arrivo e crea fino a "N" thread di lavoro alla volta (transitorio, non un pool). Ha funzionato a lungo in un sito molto grande con molti utenti. Unix ti offre gli strumenti per costruire soluzioni adeguate, devi solo metterle insieme.

    
risposta data 01.03.2018 - 15:28
fonte

Leggi altre domande sui tag