2012-12-27 10 views
11

Ho un servizio Web che crea un'attività e un'attività di continuazione.Operazione di continuazione nella stessa discussione precedente

Nel primo compito che ci Thread.CurrentPrincipal

Quindi, quando il ContinuationTask inizia non ha più il Thread.CurrentPrincipal.

vorrei specificare nel ContinuationTask che dovrebbe funzionare nello stesso thread come suo antecedente.

Ho cercato nel web ma ho trovato solo il requisito per il thread da eseguire in SynchronizationContext, quindi sto iniziando a pensare che manchi qualche regola di base, specialmente per quanto riguarda Thread.Principal dovrebbe funzionare.

+3

compiti Tying per le discussioni è una cattiva idea e soggetto a errori in caso di eccezioni. Invece di correggere il principal del thread e richiedere a tutte le attività di utilizzare lo stesso thread, provare a passare l'oggetto WindowsIDentity o token alle attività come stato e impersonare l'utente in ogni attività. Altrimenti si corre il rischio di cambiare l'identità di ThreadPool se si verifica un'eccezione e si dimentica di cancellare l'identità –

+0

PS. Che tipo di identità stai usando? WindowsIdentity o qualcos'altro? –

+1

Abbiamo implementato il nostro IPrincipal ed è l'applicazione stessa che esegue l'autenticazione. Sembra che la cosa migliore sia passare l'IPrincipal tra i compiti. –

risposta

14

Prima di tutto, non utilizzare TaskContinuationOptions.ExecuteSynchronously per questo scopo! Non è possibile forzare la continuazione sullo stesso thread. Funziona solo con una probabilità molto alta. Ci sono sempre casi in cui non funziona: troppa ricorsione farà sì che TPL non esegua in modo sincrono. I Custom TaskScheduler s non sono inoltre obbligati a supportarlo.

Questo è un malinteso comune, soprattutto perché è stato erroneamente propagato sul web. Ecco qualche lettura su questo argomento: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx

Se è necessario eseguire sullo stesso thread, fare questo:

Task.Factory.StartNew(() => { First(); Second(); }); 

Così facile.

Permettetemi di illustrare il motivo che funziona, mostrando una soluzione alternativa:

void MyCompositeTask() 
{ 
    var result = First(); 
    Second(result); 
} 
Task.Factory.StartNew(() => MyCompositeTask()); 

Questo sembra più intuitivo: Passiamo MyCompositeTask al TPL a correre. Il TPL non si cura di ciò che facciamo nel nostro callback. Possiamo fare tutto ciò che vogliamo, incluso chiamare più metodi e trasmettere i risultati.

+1

Senza offesa ma vorrei prendere gli autori di questo libro (considerato da molti essere il riferimento definitivo su C# 4.0) per essere una risorsa più affidabile di te. Fornisci del materiale di riferimento o di lettura che dimostri come la soluzione che hai fornito assicuri che i 2 metodi vengano chiamati in modo tale da soddisfare il comportamento che l'OP sta cercando. –

+0

Non sono abbastanza arrogante per non ammettere che questa è una soluzione più adatta, ma quando mi stai dicendo che gli autori di un prestigioso libro sull'argomento sono "sbagliati" mi piacerebbe vedere un po 'più di prove. E ad un certo punto il 99,9999,9% è abbastanza buono da essere considerato 100. La varianza di cui stai parlando in questo caso non sembra valere la pena di essere presa in considerazione specialmente quando la ricorsione profonda non ha alcuna influenza qui –

+2

@JesseCarter non pensi che l'esecuzione un metodo dopo l'altro garantirà che il principale persiste? * Tutto * persiste tra le ordinarie invocazioni del metodo .; La profonda ricorsione avviene molto con catene di continuazione potenzialmente infinite .; Qui ci sono più scenari in cui ExecSync non ha effetto: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx (fonte attendibile). – usr

4

Dal mio C# libro di testo (C# 4.0 in a Nutshell):

È possibile costringerli [compiti di continuazione] per eseguire sullo stesso thread [come il loro antecedente] specificando TaskContinuationOptions.ExecuteSynchronously al momento della chiamata ContinueWith: questo può migliorare le prestazioni in continuazioni a grana molto fine con riduzione della reindirizzamento.

In principio non ho provato questo, ma sembra essere quello che stai cercando e potrebbe essere utilizzato in congiunzione con Thread.CurrentPrincipal.

Ecco un link ad un MSDN article con alcuni esempi più concreti e

+1

Non puoi forzarlo. Funziona solo con una probabilità molto alta! Ci sono * sempre * casi in cui non funziona. Votato per questo. – usr

+1

Ecco un riferimento per la mia precedente affermazione: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx – usr

3

L'impostazione dell'identità di un thread di pool non è una buona idea. Ti lega a questo thread specifico e rischia di "perdere" l'identità in caso di eccezioni, se ti dimentichi di cancellare l'identità in un gestore di eccezioni. Potresti finire con attività non correlate eseguite usando l'identità "trapelata".

Provare a passare l'oggetto WindowsIdentity alle attività e impersonare utilizzando WindowsIdentity.Impersonate. Ciò consentirà di utilizzare qualsiasi thread disponibile e cancellerà in modo sicuro l'identità anche se si verifica un'eccezione.

si può provare qualcosa di simile:

WindowsPrincipal myPrincipal=...; 
... 
var identity=(WindowsIdentity)myPrincipal.Identity; 
var task=Task.Factory.StartNew(ident=>{ 
     var id=(WindowsIdentity)ident; 
     using(var context=id.Impersonate()) 
     { 
      //Work using the impersonated identity here 
     } 
     return id; 
    },identity). 
.ContinueWith(r=>{ 
     var id = r.Result; 
     using(var context=id.Impersonate()) 
     { 
      //Work using the impersonated identity here 
     } 
}); 

I using dichiarazioni assicurano che l'identità rappresentata viene eliminata anche se si verifica un'eccezione.

2

chiamata la continuazione con TaskScheduler.FromCurrentSynchronizationContext():

Task UITask= task.ContinueWith(() => 
{ 
this.TextBlock1.Text = "Complete"; 
}, TaskScheduler.FromCurrentSynchronizationContext()); 

copiato da https://stackoverflow.com/a/4331287/503969

+0

"InvalidOperationException non gestito: il SynchronizationContext corrente non può essere utilizzato come TaskScheduler." :-( – Ricibob

+1

http://stackoverflow.com/a/8246053/503969 - ti ho salvato su Google :) –

Problemi correlati