Dlaczego jest to zadanie oczekujące.Yield () " wymagane dla wątku.CurrentPrincipal to flow correctly?
Poniższy kod został dodany do świeżo stworzonego projektu Visual Studio 2012. NET 4.5 WebAPI.
Próbuję przypisać zarówno HttpContext.Current.User
jak i Thread.CurrentPrincipal
metodą asynchroniczną. Przypisanie Thread.CurrentPrincipal
przebiega nieprawidłowo, chyba że zostanie wykonane await Task.Yield();
(lub cokolwiek innego asynchronicznego) (Przejście true
do AuthenticateAsync()
zakończy się sukcesem).
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web.Http;
namespace ExampleWebApi.Controllers
{
public class ValuesController : ApiController
{
public async Task GetAsync()
{
await AuthenticateAsync(false);
if (!(User is MyPrincipal))
{
throw new System.Exception("User is incorrect type.");
}
}
private static async Task AuthenticateAsync(bool yield)
{
if (yield)
{
// Why is this required?
await Task.Yield();
}
var principal = new MyPrincipal();
System.Web.HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal = principal;
}
class MyPrincipal : GenericPrincipal
{
public MyPrincipal()
: base(new GenericIdentity("<name>"), new string[] {})
{
}
}
}
}
Uwagi:
-
await Task.Yield();
może pojawić się w dowolnym miejscu wAuthenticateAsync()
lub może zostać przeniesiony doGetAsync()
po wywołaniu doAuthenticateAsync()
i nadal się uda. -
ApiController.User
zwracaThread.CurrentPrincipal
. -
HttpContext.Current.User
zawsze płynie poprawnie, nawet bezawait Task.Yield()
. -
Web.config
zawiera<httpRuntime targetFramework="4.5"/>
które implikujeUseTaskFriendlySynchronizationContext
. - zadałem podobne pytanie kilka dni temu, ale nie zdawałem sobie sprawy, że ten przykład odnosi sukces tylko dlatego, że
Task.Delay(1000)
jest obecny.
1 answers
Ciekawe! Wygląda na to, że Thread.CurrentPrincipal
opiera się na kontekście wywołania logicznego, a nie na kontekście wywołania dla poszczególnych wątków. IMO jest to dość nieintuicyjne i byłbym ciekaw, dlaczego zostało to zaimplementowane w ten sposób.
W. NET 4.5., async
Metody oddziałują z kontekstem wywołania logicznego tak, że będzie on lepiej płynął z metodami async
. Mam wpis na blogu na temat ; AFAIK to jedyne miejsce gdzie jest to udokumentowane. W. NET 4.5, na początku każdego async
metoda aktywuje zachowanie "Kopiuj przy zapisie" dla logicznego kontekstu wywołania. Gdy (if) logiczny kontekst wywołania zostanie zmodyfikowany, najpierw utworzy lokalną kopię samego siebie.
Możesz zobaczyć "localness" kontekstu wywołania logicznego (tzn. czy został skopiowany) obserwując System.Threading.Thread.CurrentThread.ExecutionContextBelongsToCurrentScope
w oknie obserwacyjnym.
Jeśli nie Yield
, to po ustawieniu Thread.CurrentPrincipal
tworzysz kopię kontekstu wywołania logicznego, który jest traktowany jako "lokalny" dla tej metody async
. Gdy async
metoda zwraca, że lokalny kontekst jest odrzucany, a oryginalny kontekst zajmuje jego miejsce (możesz zobaczyć ExecutionContextBelongsToCurrentScope
zwracając do false
).
Z drugiej strony, jeśli zrobisz Yield
, wtedy SynchronizationContext
zachowanie przejmuje kontrolę. To, co się dzieje, to to, że HttpContext
jest przechwytywane i używane do wznowienia obu metod. W tym przypadku, jesteś Nie widząc Thread.CurrentPrincipal
zachowane od AuthenticateAsync
do GetAsync
; to, co się dzieje, jest HttpContext
jest zachowana, a następnie HttpContext.User
nadpisuje Thread.CurrentPrincipal
przed metody wznowić.
Jeśli przeniesiesz Yield
do GetAsync
, zobaczysz podobne zachowanie: {[0] } jest traktowana jako lokalny zakres modyfikacji do AuthenticateAsync
; odwraca swoją wartość, gdy ta metoda powróci. Jednak {[18] } jest nadal ustawiona poprawnie, a wartość ta zostanie przechwycona przez Yield
i gdy metoda zostanie wznowiona, zastąpi ona Thread.CurrentPrincipal
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 17:15:03