2012-12-27 9 views
11

Mam usługę WebService, która tworzy zadanie i zadanie kontynuacji.Kontynuacja Zadanie w tym samym temacie, co poprzednie

W pierwszym zadaniu możemy ustawić Thread.CurrentPrincipal

Stąd Gdy ContinuationTask zaczyna to już nie ma Thread.CurrentPrincipal.

Chciałbym określić w ContinuationTask że powinien działać w tym samym wątku jako jego poprzednika.

Szukałem w Internecie, ale znalazłem tylko wymaganie, aby wątek działał w SynchronizationContext, dlatego zaczynam myśleć, że brakuje mi podstawowej zasady, szczególnie odnośnie tego, jak powinna działać Thread.Principal.

+3

wiążąc zadań wątków jest to zły pomysł i podatne na błędy w przypadku wyjątków. Zamiast naprawiać główny wątek i wymagać, aby wszystkie zadania używały tego samego wątku, spróbuj przekazać obiekt WindowsID lub token do zadań jako stan i podszyć się pod użytkownika w każdym zadaniu. W przeciwnym razie istnieje ryzyko zmiany tożsamości ThreadPool w przypadku wystąpienia wyjątku i zapomnienia o usunięciu tożsamości –

+0

PS. Jakiego rodzaju tożsamości używasz? WindowsIdentity czy coś innego? –

+1

Wdrożyliśmy nasz własny IPrincipal i to sama aplikacja dokonuje uwierzytelnienia. Wydaje się, że najlepiej jest przekazać IPrincipal wśród zadań. –

Odpowiedz

14

Przede wszystkim, nie należy używać do tego celu TaskContinuationOptions.ExecuteSynchronously! Nie możesz wymusić kontynuacji w tym samym wątku. Działa tylko z bardzo dużym prawdopodobieństwem. Zawsze są przypadki, w których to nie działa: zbyt duża rekurencja spowoduje, że TPL nie będzie wykonywać synchronicznie. Niestandardowe TaskScheduler s również nie są zobowiązane do wspierania tego.

Jest to powszechne nieporozumienie, zwłaszcza że jest błędnie propagowane w Internecie. Oto niektóre czytanie na ten temat: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx

Jeśli trzeba uruchomić w tym samym wątku, to zrobić:

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

tak łatwo.

Pozwól mi wyjaśnić, dlaczego to działa, pokazując alternatywne rozwiązanie:

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

To wygląda bardziej intuicyjny: Mijamy MyCompositeTask z TPL do uruchomienia. TPL nie dba o to, co robimy w naszym callbacku. Możemy robić, co chcemy, w tym wywoływać wiele metod i przekazywać wyniki.

+1

Bez urazy, ale wziąłbym autorów tej książki (uważanej przez wielu być ostatecznym odnośnikiem na C# 4.0), aby być bardziej wiarygodnym zasobem niż ty. Proszę podać referencje lub materiały do ​​czytania, demonstrujące, w jaki sposób podane przez ciebie rozwiązanie zapewnia, że ​​2 metody będą wywoływane w sposób, który spełnia zachowanie, jakiego szuka OP. –

+0

Nie jestem wystarczająco arogancki, aby nie przyznać, że jest to bardziej odpowiednie rozwiązanie, ale kiedy mówisz mi, że autorzy prestiżowej książki na ten temat są "źli", chciałbym zobaczyć trochę więcej dowodów. I w pewnym momencie 99,999999% jest wystarczająco dobre, aby można je było uznać za 100. Wariancja, o której mówisz w tym przypadku, nie wydaje się być warta rozważenia, szczególnie gdy głębokie rekursje nie mają tu znaczenia –

+2

@JesseCarter nie sądzisz, że wykonanie jedna metoda po drugiej gwarantuje, że zleceniodawca nadal będzie istniał? * Wszystko * pozostaje między zwykłymi wywołaniami metod. Głębokie rekursje zdarzają się z potencjalnie nieskończonymi łańcuchami kontynuacji; Oto kilka scenariuszy, w których ExecSync nie wchodzi w życie: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx (zaufane źródło). – usr

4

Z mojego C# podręcznika (C# 4.0 w skrócie):

można zmusić [Zadania kontynuacja], aby wykonać w tym samym wątku [jako swojego poprzednika] określając TaskContinuationOptions.ExecuteSynchronously Dzwoniąc ContinueWith: może to poprawiają wydajność w bardzo drobnoziarnistych kontynuacjach z redukcją dwukierunkowości.

Zasadniczo nie próbowałem tego, ale wydaje się, że jest to, czego szukasz i może być używany w połączeniu z Thread.CurrentPrincipal.

Tu jest link do MSDN article z niektórych bardziej konkretnych przykładach, jak również

+1

Nie możesz tego wymusić. Działa tylko z bardzo dużym prawdopodobieństwem! Istnieją * zawsze * przypadki, w których to nie działa. Z tego powodu przegłosowano. – usr

+1

Oto odniesienie do mojego poprzedniego oświadczenia: http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx – usr

3

Ustawienie tożsamości nici w basenie nie jest dobrym pomysłem. Przywiązuje cię do tego konkretnego wątku i ryzykuje "wyciekaniem" tożsamości w przypadku wyjątków, jeśli zapomnisz wyczyścić tożsamość w procedurze obsługi wyjątków. Możesz skończyć z niepowiązanymi zadaniami działającymi przy użyciu "wyciekłej" tożsamości.

Spróbuj przekazać obiekt WindowsIdentity do zadań i podszywać się pod numer WindowsIdentity.Impersonate. Umożliwi to użycie dowolnego dostępnego wątku i bezpiecznie usunie tożsamość nawet w przypadku wystąpienia wyjątku.

Można spróbować czegoś takiego:

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 
     } 
}); 

W using oświadczenia zapewnić, że wcieliła tożsamość jest wyczyszczone, nawet jeśli wystąpi wyjątek.

2

połączeń kontynuacja z TaskScheduler.FromCurrentSynchronizationContext():

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

Skopiowane z https://stackoverflow.com/a/4331287/503969

+0

"Nieprawidłowy wyjątek InvalidOperationException: bieżący kontekst synchronizacji nie może być używany jako TaskScheduler." :-( – Ricibob

+1

http://stackoverflow.com/a/8246053/503969 - uratowałem Cię Googlując :) –

Powiązane problemy