且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

设置与Thread.CurrentPrincipal中异步/的await

更新时间:2023-12-03 19:08:28

您可以使用自定义awaiter流动 CurrentPrincipal (或任何线程性能,对于这个问题)。下面的例子说明它可能怎么做,由斯蒂芬Toub的 CultureAwaiter 。它采用 TaskAwaiter 内部,所以同步上下文(如果有的话)将被捕获了。

用法:

  Console.WriteLine(Thread.CurrentPrincipal.GetType()名称。);等待TaskExt.RunAndFlowPrincipal(()=>
{
    = Thread.CurrentPrincipal中新UserPrincipal(Thread.CurrentPrincipal.Identity);
    Console.WriteLine(Thread.CurrentPrincipal.GetType()名称。);
    返回42;
});Console.WriteLine(Thread.CurrentPrincipal.GetType()名称。);

code(只有非常轻微的测试):

 公共静态类TaskExt
{
    //流动Thread.CurrentPrincipal中
    公共静态FlowingAwaitable< TResult,IPrincipal的> RunAndFlowPrincipal< TResult>(
        FUNC< TResult> FUNC,
        的CancellationToken令牌=默认(的CancellationToken))
    {
        返回RunAndFlow(
            FUNC,
            ()=> Thread.CurrentPrincipal中,
            S => = Thread.CurrentPrincipal中S,
            令牌);
    }    //任何流动
    公共静态FlowingAwaitable< TResult,TSTATE> RunAndFlow< TResult,TSTATE>(
        FUNC< TResult> FUNC,
        FUNC<&TSTATE GT; saveState和,
        动作<&TSTATE GT; restoreState,
        的CancellationToken令牌=默认(的CancellationToken))
    {
        //与FUNC2包FUNC捕捉和传播异常
        FUNC&LT元组LT; FUNC< TResult>中TSTATE>> FUNC2 =()=>
        {
            FUNC< TResult>的getResult;
            尝试
            {
                VAR的结果= FUNC();
                的getResult =()=>结果;
            }
            赶上(异常前)
            {
                //捕捉异常
                VAR EDI = ExceptionDispatchInfo.Capture(除息);
                的getResult =()=>
                {
                    //重新抛出异常捕获
                    edi.Throw();
                    //不应该达到这一点,
                    //但是没有它编译什么我们
                    //此处返回一个虚拟TResult值
                    抛出新AggregateException(edi.SourceException);
                };
            }
            返回新行<&Func键LT; TResult>中TSTATE>(的getResult,saveState和());
        };        返回新FlowingAwaitable< TResult,TSTATE>(
            Task.Run(FUNC2,令牌)
            restoreState);
    }    公共类FlowingAwaitable< TResult,TSTATE> :
        ICriticalNotifyCompletion
    {
        只读TaskAwaiter&LT元组LT; Func键< TResult>中TSTATE>> _服务员;
        只读动作<&TSTATE GT; _restoreState;        公共FlowingAwaitable(
            任务&LT元组LT; Func键< TResult>中TSTATE>>任务,
            动作<&TSTATE GT; restoreState)
        {
            _awaiter = task.GetAwaiter();
            _restoreState = restoreState;
        }        公共FlowingAwaitable< TResult,TSTATE> GetAwaiter()
        {
            返回此;
        }        公共BOOL IsCompleted
        {
            {返回_awaiter.IsCompleted; }
        }        公共TResult调用getResult()
        {
            VAR的结果= _awaiter.GetResult();
            _restoreState(result.Item2);
            返回result.Item1();
        }        公共无效OnCompleted(动作续)
        {
            _awaiter.OnCompleted(续)
        }        公共无效UnsafeOnCompleted(动作续)
        {
            _awaiter.UnsafeOnCompleted(续)
        }
    }
}

Below is a simplified version of where I am trying to set Thread.CurrentPrincipal within an async method to a custom UserPrincipal object but the custom object is getting lost after leaving the await even though it's still on the new threadID 10.

Is there a way to change Thread.CurrentPrincipal within an await and use it later without passing it in or returning it? Or is this not safe and should never be async? I know there are thread changes but thought async/await would handle synching this for me.

[TestMethod]
public async Task AsyncTest()
{
    var principalType = Thread.CurrentPrincipal.GetType().Name;
    // principalType = WindowsPrincipal
    // Thread.CurrentThread.ManagedThreadId = 11

    await Task.Run(() =>
    {
        // Tried putting await Task.Yield() here but didn't help

        Thread.CurrentPrincipal = new UserPrincipal(Thread.CurrentPrincipal.Identity);
        principalType = Thread.CurrentPrincipal.GetType().Name;
        // principalType = UserPrincipal
        // Thread.CurrentThread.ManagedThreadId = 10
    });
    principalType = Thread.CurrentPrincipal.GetType().Name;
    // principalType = WindowsPrincipal (WHY??)
    // Thread.CurrentThread.ManagedThreadId = 10
}

You could use a custom awaiter to flow CurrentPrincipal (or any thread properties, for that matter). The below example shows how it might be done, inspired by Stephen Toub's CultureAwaiter. It uses TaskAwaiter internally, so synchronization context (if any) will be captured, too.

Usage:

Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);

await TaskExt.RunAndFlowPrincipal(() => 
{
    Thread.CurrentPrincipal = new UserPrincipal(Thread.CurrentPrincipal.Identity);
    Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);
    return 42;
});

Console.WriteLine(Thread.CurrentPrincipal.GetType().Name);

Code (only very slightly tested):

public static class TaskExt
{
    // flowing Thread.CurrentPrincipal
    public static FlowingAwaitable<TResult, IPrincipal> RunAndFlowPrincipal<TResult>(
        Func<TResult> func,
        CancellationToken token = default(CancellationToken))
    {
        return RunAndFlow(
            func,
            () => Thread.CurrentPrincipal, 
            s => Thread.CurrentPrincipal = s,
            token);
    }

    // flowing anything
    public static FlowingAwaitable<TResult, TState> RunAndFlow<TResult, TState>(
        Func<TResult> func,
        Func<TState> saveState, 
        Action<TState> restoreState,
        CancellationToken token = default(CancellationToken))
    {
        // wrap func with func2 to capture and propagate exceptions
        Func<Tuple<Func<TResult>, TState>> func2 = () =>
        {
            Func<TResult> getResult;
            try
            {
                var result = func();
                getResult = () => result;
            }
            catch (Exception ex)
            {
                // capture the exception
                var edi = ExceptionDispatchInfo.Capture(ex);
                getResult = () => 
                {
                    // re-throw the captured exception 
                    edi.Throw(); 
                    // should never be reaching this point, 
                    // but without it the compiler whats us to 
                    // return a dummy TResult value here
                    throw new AggregateException(edi.SourceException);
                }; 
            }
            return new Tuple<Func<TResult>, TState>(getResult, saveState());    
        };

        return new FlowingAwaitable<TResult, TState>(
            Task.Run(func2, token), 
            restoreState);
    }

    public class FlowingAwaitable<TResult, TState> :
        ICriticalNotifyCompletion
    {
        readonly TaskAwaiter<Tuple<Func<TResult>, TState>> _awaiter;
        readonly Action<TState> _restoreState;

        public FlowingAwaitable(
            Task<Tuple<Func<TResult>, TState>> task, 
            Action<TState> restoreState)
        {
            _awaiter = task.GetAwaiter();
            _restoreState = restoreState;
        }

        public FlowingAwaitable<TResult, TState> GetAwaiter()
        {
            return this;
        }

        public bool IsCompleted
        {
            get { return _awaiter.IsCompleted; }
        }

        public TResult GetResult()
        {
            var result = _awaiter.GetResult();
            _restoreState(result.Item2);
            return result.Item1();
        }

        public void OnCompleted(Action continuation)
        {
            _awaiter.OnCompleted(continuation);
        }

        public void UnsafeOnCompleted(Action continuation)
        {
            _awaiter.UnsafeOnCompleted(continuation);
        }
    }
}