简体   繁体   中英

Using Thread.CurrentPrincipal with async and await?

Consider the following code that sets a ClaimsPrincipal on a single, main thread of execution and then runs a task and tries to access the ClaimsPrincipal:

public class Program
{
    public static void Main(string[] args)
    {
        //Setting the CurrentPrincipal on the main thread
        Thread.CurrentPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("name", "bob"), new Claim("role", "admin") }, "CUSTOM", "name", "role"));
        Console.WriteLine("Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));

            AsyncHelper.RunSync(
            async () =>
            {
                Console.WriteLine("\tInside Async Method - Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("\tInside Async Method - Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));

                //Simulate long(er) running work
                await Task.Delay(2000);

                Console.WriteLine("\tInside Async Method - Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("\tInside Async Method - Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
            });

        Console.WriteLine("Thread.CurrentThread.ManagedThreadId " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Thread.CurrentPrincipal?.Identity?.Name " + (Thread.CurrentPrincipal?.Identity?.Name ?? "null"));
    }
}

and

internal static class AsyncHelper
{
    private static readonly TaskFactory MyTaskFactory = new
        TaskFactory(CancellationToken.None,
            TaskCreationOptions.None,
            TaskContinuationOptions.None,
            TaskScheduler.Default);

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper.MyTaskFactory
                .StartNew<Task>(func)
                .Unwrap()
                .GetAwaiter()
                .GetResult();
    }
}

The output of the program is

Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
        Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
        Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name null
        Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
        Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name null
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Press any key to exit

What I thought I would see is this:

Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
        Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
        Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name bob <--- Notice here
        Inside Async Method - Thread.CurrentThread.ManagedThreadId 3
        Inside Async Method - Thread.CurrentPrincipal?.Identity?.Name bob <--- Notice here
Thread.CurrentThread.ManagedThreadId 2
Thread.CurrentPrincipal?.Identity?.Name bob
Press any key to exit

What happened to the ClaimsPrincipal that was set on the main thread (in the case of this specific output ManagedThreadId 2)? Why did the ClaimsPrincipal not get copied to the other thread when the ExecutionContext was copied?

Update : The .Net target framework is .NET Core 2.0.

Update 2 : This issue seems to be specific to .NET Core. Using the same code but instead targeting the .NET 4.6.1 framework I get the output I expect.

You just can't rely on thread properties, since such details as threads assignment to the task depends on concrete ThreadScheduler ( https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx ) . There are many different ThreadScheduler implementations.

There is an option to implement your own custom TaskScheduler where you will set Thread.CurrentPrincipal to your values, but better just do not rely on all threading properties at all staying on "task level".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM