2009-11-05 14 views
8

Ho un'applicazione desktop che gira su una rete e ogni istanza si connette allo stesso database.Come mutare su una rete?

Quindi, in questa situazione, come è possibile implementare un mutex che funzioni su tutte le istanze in esecuzione connesse allo stesso database?

In altre parole, non voglio che due istanze + eseguano la stessa funzione nello stesso momento. Se uno sta già eseguendo la funzione, le altre istanze non dovrebbero accedervi.


PS: la transazione del database non verrà risolta, poiché la funzione che non voglio mutex non utilizza il database. Ho menzionato il database solo perché può essere utilizzato per scambiare informazioni tra le istanze in esecuzione.

PS2: la funzione richiede circa ~ 30 minuti per essere completata, quindi se una seconda istanza tenta di eseguire la stessa funzione, vorrei visualizzare un messaggio piacevole che non può essere eseguita in questo momento perché il computer 'X' è già eseguendo quella funzione.

PS3: la funzione deve essere elaborata sul computer client, quindi non è possibile utilizzare stored procedure.

+2

Qual è il tuo database e supporta le transazioni? – ephemient

risposta

2

Penso che stiate cercando una transazione di database. Una transazione isolerà le tue modifiche da tutti gli altri client.

Aggiornamento: Hai menzionato che la funzione non scrive attualmente nel database. Se si desidera mutuare questa funzione, ci deve essere una posizione centrale per memorizzare il titolare corrente mutex. Il database può funzionare per questo - basta aggiungere una nuova tabella che include il nomecomputer dell'attuale titolare. Controlla quella tabella prima di iniziare la tua funzione.

Penso che la tua domanda potrebbe essere una confusione però. I mutex dovrebbero riguardare la protezione delle risorse. Se la tua funzione non sta accedendo al database, quale risorsa condivisa stai proteggendo?

+0

In realtà, non sto cercando una transazione di database. Per favore, vedi gli aggiornamenti nella domanda. –

+4

Per fare il mutex, avrai bisogno di * qualche * tipo di comunicazione tra i tuoi clienti e un punto centrale. Se hai già una connessione al database, che include un'implementazione mutex ** molto ** buona (i database sono pensati per questa roba), perché non usarla? – Wim

0

inserire il codice all'interno di una transazione - nell'app o meglio - all'interno di una stored procedure e chiamare la stored procedure. il meccanismo di transazione isolerà il codice tra i chiamanti.

0

Considerare invece una coda di messaggi. Come accennato, il DB dovrebbe gestire tutto questo per le transazioni o l'accesso seriale alle tabelle (ala MyISAM).

0

In passato ho fatto quanto segue:

  1. Creare una tabella che ha fondamentalmente due campi, function_name e is_running
  2. Non so cosa RDBMS che si sta utilizzando, ma la maggior parte hanno una modo per bloccare singoli record per l'aggiornamento.Ecco alcuni pseduocode basata su Oracle:

    BEGIN TRANS

    SELECT PER AGGIORNAMENTO is_running FROM WHERE function_table function_name = 'pippo';

    - Controllare qui per vedere se è in esecuzione, in caso contrario, è possibile impostare l'esecuzione di 'vero'

    UPDATE set function_table is_running = 'Y', dove function_name = 'pippo';

    COMMETTERE TRANS

Ora non ho la documentazione Oracle PSQL con me, ma si ottiene l'idea. La clausola 'FOR UPDATE' blocca lì il record dopo la lettura fino al commit, quindi altri processi bloccheranno su quell'istruzione SELECT fino a quando il processo corrente non verrà eseguito.

+1

Cosa succede se l'istanza in esecuzione si arresta in modo anomalo nel mezzo della funzione e non ottiene mai una possibilità di ser is_running = false? –

+0

In casi come quello di solito inserisco anche un timestamp nella tabella in modo che se una riga ha un timestamp troppo vecchio, allora presumo che l'istanza successiva possa avviare la funzione da zero. –

0

È possibile utilizzare Terracotta per implementare tale funzionalità, se si dispone di uno stack Java.

+0

Cos'è la terracotta? –

+0

Sembra troppo. Inoltre, Java non è disponibile. –

0

Anche se la funzione non utilizza attualmente il database, è comunque possibile risolvere il problema con una tabella specifica allo scopo di sincronizzare questa funzione. Le specifiche dipendono dal DB e dal modo in cui gestisce i livelli di isolamento e il blocco. Ad esempio, con SQL Server si imposta l'isolamento della transazione su letture ripetibili, si legge un valore dalla riga di blocco e lo si aggiorna all'interno di una transazione. Non eseguire il commit della transazione fino al completamento della tua funzione. È inoltre possibile utilizzare blocchi tabella espliciti in una transazione sulla maggior parte dei database, che potrebbe essere più semplice. Questa è probabilmente la soluzione più semplice dato che stai già usando un database.

Se non si vuole fare affidamento sul database per qualsiasi motivo si potrebbe scrivere un semplice servizio che avrebbe accettato connessioni TCP dal client. Ogni client richiederebbe il permesso di essere eseguito e restituirebbe una risposta al termine. Il server sarebbe in grado di garantire che solo un client ottenga l'autorizzazione per l'esecuzione alla volta. I client morti alla fine eliminerebbero la connessione TCP e verrebbero rilevati fino a quando si mantengono le corrette impostazioni keep-alive.

Anche la soluzione di coda dei messaggi suggerita da Xepoch funzionerebbe. È possibile utilizzare qualcosa come MSMQ o Java Message Queue e disporre di un singolo messaggio che funga da token di esecuzione. Tutti i tuoi clienti richiederebbero il messaggio e poi lo ripubblicheranno al termine. Rischiate una situazione di stallo se un cliente muore prima di ripubblicare, quindi è necessario elaborare una logica per rilevarlo e potrebbe complicarsi.

+0

La soluzione DB è come quella che mjmarsh ha pubblicato, ma non si basa sul valore di un flag che deve essere ripristinato. La tua funzione viene eseguita mentre la transazione è aperta e il valore nella tabella non ha importanza.Per modificare il suo esempio: BEGIN TRANS SELECT FOR UPDATE lock FROM function_table WHERE function_name = 'foo'; UPDATE set_table set lock = lock + 1 dove function_name = 'foo'; - Elaborazione della funzione qui COMMIT TRANS –

Problemi correlati