2009-07-24 19 views
7

Il mio scenario: un server e alcuni client (anche se non molti). Il server può rispondere solo a un client alla volta, quindi devono essere messi in coda. Sto usando un mutex (boost::interprocess::interprocess_mutex) per fare questo, avvolto in un boost::interprocess::scoped_lock.Come assumere la proprietà di un boost abbandonato :: interprocess :: interprocess_mutex?

Il problema è che se un client muore in modo imprevisto (cioè non esegue il distruttore) mentre tiene il mutex, gli altri client sono in difficoltà, perché stanno aspettando quel mutex. Ho preso in considerazione l'utilizzo di attesa temporizzata, quindi se il client aspetta, diciamo, 20 secondi e non ottiene il mutex, va avanti e parla comunque al server.

Problemi con questo approccio: 1) lo fa ogni volta. Se si trova in un ciclo, parlando costantemente al server, è necessario attendere il timeout ogni volta. 2) Se ci sono tre client, e uno di loro muore tenendo premuto il mutex, gli altri due attenderanno solo 20 secondi e parleranno allo stesso tempo al server - esattamente quello che stavo cercando di evitare.

Quindi, come posso dire a un cliente, "ehi, sembra che questo mutex sia stato abbandonato, prendine possesso di esso"?

+2

Se si sta facendo affidamento sui client per eseguire la sincronizzazione, lo si sta facendo all'indietro. Dovresti davvero aggiustare il tuo server in modo che possa accettare più connessioni, anche se fa in modo che le altre connessioni rimangano in attesa mentre serve una alla volta. Ciò ti consente di prendere la parte * interprocess * dall'equazione. –

+0

Punto giusto. Tuttavia, la mia applicazione è stata originariamente specificata per avere un solo cliente alla volta - solo recentemente (come oggi) ho scoperto che potrebbero esserci più client. Ho cercato di risolverlo nel modo più semplice, ma suppongo che dovrò inventare qualcosa di più sofisticato. –

+0

Sembra che l'intero meccanismo del mutex sia difettoso senza un meccanismo di recupero. Wish Boost risolve questo problema. – balki

risposta

6

Sfortunatamente, questo non è supportato dall'interfaccia boost :: interprocess as-is. Esistono tuttavia alcuni modi per implementarlo:

Se ci si trova su una piattaforma POSIX con supporto per pthread_mutexattr_setrobust_np, modificare boost/interprocess/sync/posix/thread_helpers.hpp e boost/interprocess/sync/posix/interprocess_mutex. hpp per usare robusti mutex e per gestire in qualche modo il ritorno di EOWNERDEAD da pthread_mutex_lock.

Se ci si trova su un'altra piattaforma, è possibile modificare boost/interprocess/sync/emulation/interprocess_mutex.hpp per utilizzare un contatore di generazione, con il flag bloccato nel bit inferiore. Quindi puoi creare un protocollo di recupero che imposterà un flag nella parola di blocco per indicare un reclamo in sospeso, quindi fare un confronto e scambio dopo un timeout per verificare che la stessa generazione sia ancora nella parola di blocco, e in tal caso sostituire con un valore di prossima generazione bloccato.

Se sei su Windows, un'altra buona opzione sarebbe quella di utilizzare oggetti mutex nativi; probabilmente saranno più efficienti degli indaffarati ad aspettare comunque.

Si potrebbe anche voler riconsiderare l'uso di un protocollo di memoria condivisa - perché non utilizzare invece un protocollo di rete?

+0

Ottima risposta. Non penso che lo applicherò, comunque; non sembra valere la pena - penserò a qualcos'altro. Per quanto riguarda il tuo suggerimento di utilizzare un protocollo di rete, non potrei essere più d'accordo con te. Sfortunatamente, è troppo tardi per cambiare le cose in modo così radicale. –

Problemi correlati