更新时间: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);
}
}
}