2012-12-16 16 views
18

Ho scritto un controller e un'azione che utilizzo come servizio. Questo servizio esegue un'azione piuttosto costosa. Vorrei limitare l'accesso a questa azione se c'è già un'azione in corso.come bloccare un'azione asp.net mvc?

C'è un modo integrato per bloccare un'azione asp.net mvc?

Grazie

+0

Vuoi limitarlo per utente, per sessione, per applicazione? – Oshry

+1

Stai cercando code. –

+0

limitazione generale, non consentire a nessuno di entrare se è stato chiamato – vondip

risposta

38

Stai cercando qualcosa di simile?

public MyController : Controller 
{ 
    private static object Lock = new object(); 

    public ActionResult MyAction() 
    { 
     lock (Lock) 
     { 
      // do your costly action here 
     }  
    } 
} 

che questo impedirà qualsiasi altri thread di eseguire un'azione se un thread sta elaborando codice all'interno del blocco lock.

Aggiornamento: ecco come funziona

Codice del metodo viene sempre eseguito da un thread. Su un server pesantemente caricato, è possibile inserire 2 o più thread diversi e iniziare l'esecuzione di un metodo in parallelo. Secondo la domanda, questo è quello che vuoi prevenire.

Nota come l'oggetto private Lock è static. Ciò significa che è condiviso tra tutte le istanze del tuo controller. Quindi, anche se ci sono 2 istanze di questo controller costruito nell'heap, entrambi condividono lo stesso oggetto Lock. (L'oggetto non deve nemmeno essere chiamato Lock, potresti chiamarlo Jerry o Samantha e servirebbe sempre allo stesso scopo.)

Ecco cosa succede. Il processore può consentire solo 1 thread per inserire una sezione di codice alla volta. In circostanze normali, il thread A potrebbe iniziare l'esecuzione di un blocco di codice e quindi il thread B potrebbe iniziare ad eseguirlo. Quindi in teoria puoi avere 2 thread che eseguono lo stesso metodo (o qualsiasi blocco di codice) allo stesso tempo.

La parola chiave lock può essere utilizzata per impedire questo. Quando un thread entra in un blocco di codice racchiuso in una sezione lock, "preleva" l'oggetto lock (ciò che è tra parentesi dopo la parola chiave lock, a.k.a Lock, Jerry o Samantha, che dovrebbe essere contrassegnato come campo static). Per la durata di tempo in cui viene eseguita la sezione bloccata, "trattiene" l'oggetto di blocco. Quando il thread esce dalla sezione bloccata, "rinuncia" all'oggetto lock. Dal momento in cui il thread preleva l'oggetto lock, fino a quando non abbandona l'oggetto lock, tutti gli altri thread non possono accedere alla sezione di codice bloccata. In effetti, vengono "messi in pausa" finché il thread attualmente in esecuzione non abbandona l'oggetto lock.

Quindi il thread A preleva l'oggetto di blocco all'inizio del metodo MyAction. Prima di abbandonare l'oggetto di blocco, il thread B tenta anche di eseguire questo metodo. Tuttavia, non può raccogliere l'oggetto lock perché è già trattenuto dal thread A. Quindi attende il thread A per rinunciare all'oggetto lock.Quando lo fa, il thread B preleva l'oggetto lock e inizia l'esecuzione del blocco di codice. Quando il thread B ha terminato di eseguire il blocco, si arresta l'oggetto lock per il thread successivo che è delegato a gestire questo metodo.

... ma non sono sicuro se questo è quello che stai cercando ...

Usando questo approccio non sarà necessariamente rendere il codice eseguito più velocemente. Assicura solo che un blocco di codice possa essere eseguito solo da 1 thread alla volta. Di solito è usato per ragioni di concorrenza, non per ragioni di prestazioni. Se puoi fornire maggiori informazioni sul tuo problema specifico nella domanda, potrebbe esserci una risposta migliore di questa.

Ricordare che il codice presentato sopra farà attendere altri thread prima di eseguire il blocco. Se questo non è quello che vuoi, e vuoi che l'intera azione venga "saltata" se è già stata eseguita da un altro thread, allora usa qualcosa di più simile alla risposta di Oshry. È possibile memorizzare queste informazioni nella cache, nella sessione o in qualsiasi altro meccanismo di archiviazione dei dati.

+0

non dovrebbe essere bloccato anche in una sessione o in un oggetto cache? – vondip

+0

Perché pensi che dovrebbe essere necessario mantenere la sessione o la cache? – danludwig

+0

poiché http non è session-less, dovrei salvare il blocco durante le sessioni, no? – vondip

2

È possibile creare un attributo personalizzato come [UseLock] secondo le vostre esigenze e metterlo prima azione

0

Il modo più semplice per farlo sarebbe Salva nella cache un valore booleano che indica l'azione è è già in esecuzione il BL richiesto:

if (System.Web.HttpContext.Current.Cache["IsProcessRunning"]) 
{ 
    System.Web.HttpContext.Current.Cache["IsProcessRunning"] = true; 
    // run your logic here 
    System.Web.HttpContext.Current.Cache["IsProcessRunning"] = false 
} 

Ovviamente si può fare questo, o qualcosa di simile, come pure un attributo.

+3

si potrebbe avere i due thread riuniti all'interno della condizione di if. non è abbastanza buono temo – vondip

+0

Come puoi ottenere due thread riuniti all'interno della condizione if? È praticamente impossibile, ma se si deve essere assolutamente sicuri, è possibile bloccare l'assegnazione alla cache. – Oshry

+6

Questa è la ricetta per un bug di condizioni di gara molto sporco. Se succede qualcosa di brutto se due thread eseguono la stessa cosa, usa un lucchetto. – Vincent

5

Dopo aver letto e accettato la risposta di cui sopra ho voluto una soluzione leggermente diversa: Se si desidera rilevare una seconda chiamata ad un'azione, utilizzare Monitor.TryEnter:

if (!Monitor.TryEnter(Lock, new TimeSpan(0))) 
{ 
    throw new ServiceBusyException("Locked!"); 
} 
try 
{ 
... 
} 
finally { 
    Monitor.Exit(Lock); 
} 

utilizzare lo stesso oggetto di blocco statico come dettagliato da @danludwig

0

ho suggerimenti a tale proposito.

1- https://github.com/madelson/DistributedLock sistema ampia soluzione serratura

2- Hangfire BackgroundJob.Enqueue con [DisableConcurrentExecution (1000)] attributo.

Due soluzioni sono in attesa di completamento del processo. Non voglio generare errori quando richiesto allo stesso tempo.