这是一个复杂的情况,请容忍我。
我有一个BagOfProperties
类,我想创建它的一个示例AsyncLocal
,如下所示
private readonly static System.Threading.AsyncLocal<BagOfProperties> _bag = new();
private static BagOfProperties Bag => _bag.Value ??= new BagOfProperties();
在代码中的每个地方,我现在都通过Bag
变量访问属性,很好地使它们都成为AsyncLocal。
现在,由于 * 原因 *,我希望能够重置这些属性。很容易做到:
private static void ResetBag()
{
_bag.Value = null;
}
由于_bag
是一个AsyncLocal
,这在任何时候都是安全的,使用包的现有任务仍然可以访问旧值,但新任务将首先调用构造函数。
这部分工作。
下一步是,我想从WinForms的Application.Idle
事件处理程序调用ResetBag
。这是它中断的地方(在某些比赛条件下-取决于我放置断点的位置,它可能会工作)。
发生的情况是,_bag.Value
被设置为null
,但随后我们向上移动堆栈,直到System.Threading.ExecutionContext.RunInternal
,其中System.Threading.ExecutionContextSwitcher.Undo()
被调用,_bag.Value
被恢复到以前的版本。
整个堆栈上甚至没有一个异步调用!
我添加了以下ValueChangedHandler。
private static void ValueChangedHandler(System.Threading.AsyncLocalValueChangedArgs<BagOfProperties> asyncLocalValueChangedArgs)
{
if (System.Threading.Thread.CurrentThread.ManagedThreadId == 1)
{
var previousValue = asyncLocalValueChangedArgs.PreviousValue;
var currentValue = asyncLocalValueChangedArgs.CurrentValue;
var contextChanged = asyncLocalValueChangedArgs.ThreadContextChanged;
}
}
我只对ManagedThreadId == 1
感兴趣,因为现在在我的代码中没有异步,但是在其他线程上的第三方组件中发生了一些事情。
当值设置为null时,堆栈看起来像这样(星号是我的)。
MyHelper.dll!MyApp.Class1.ValueChangedHandler(System.Threading.AsyncLocalValueChangedArgs<MyApp.Class1.BagOfProperties> asyncLocalValueChangedArgs = {System.Threading.AsyncLocalValueChangedArgs<MyApp.Class1.BagOfProperties>}) Line 28 C#
mscorlib.dll!System.Threading.AsyncLocal<MyApp.Class1.BagOfProperties>.System.Threading.IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged) Unknown
mscorlib.dll!System.Threading.ExecutionContext.SetLocalValue(System.Threading.IAsyncLocal local, object newValue, bool needChangeNotifications) Unknown
mscorlib.dll!System.Threading.AsyncLocal<System.__Canon>.Value.set(System.__Canon value) Unknown
MyHelper.dll!MyApp.Class1.ResetBag() Line 21 C#
[Native to Managed Transition]
[Managed to Native Transition]
* MyFramework.Win.dll!MyFramework.MyUtils.Application_Idle(object sender = {System.Threading.Thread}, System.EventArgs e = {System.EventArgs}) Line 3872 C#
System.Windows.Forms.dll!System.Windows.Forms.Application.RaiseIdle(System.EventArgs e) Unknown
MyFramework.Win.dll!MyFramework.MyUtils.TrackPopupMenu.AnonymousMethod__136_1() Line 3697 C#
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackDo(System.Windows.Forms.Control.ThreadMethodEntry tme) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(object obj) Unknown
* mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks() Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) Unknown
MyFramework.Win.dll!MyFramework.MyForm.WndProc(ref System.Windows.Forms.Message m = {System.Windows.Forms.Message}) Line 4309 C#
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 49641, System.IntPtr wparam, System.IntPtr lparam) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason = -1, int pvLoopData = 0) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = -1, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.ApplicationContext}) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) Unknown
MyApp.exe!MyApp.Program.Main() Line 25 C#
当值被还原时,堆栈看起来像这样。
MyHelper.dll!MyApp.Class1.ValueChangedHandler(System.Threading.AsyncLocalValueChangedArgs<MyApp.Class1.BagOfProperties> asyncLocalValueChangedArgs = {System.Threading.AsyncLocalValueChangedArgs<MyApp.Class1.BagOfProperties>}) Line 28 C#
mscorlib.dll!System.Threading.AsyncLocal<MyApp.Class1.BagOfProperties>.System.Threading.IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged) Unknown
mscorlib.dll!System.Threading.ExecutionContext.OnAsyncLocalContextChanged(System.Threading.ExecutionContext previous = {System.Threading.ExecutionContext}, System.Threading.ExecutionContext current = {System.Threading.ExecutionContext}) Unknown
mscorlib.dll!System.Threading.ExecutionContextSwitcher.Undo() Unknown
* mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks() Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.ScrollableControl.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) Unknown
MyFramework.Win.dll!MyFramework.MyForm.WndProc(ref System.Windows.Forms.Message m = {System.Windows.Forms.Message}) Line 4309 C#
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.OnMessage(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) Unknown
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 49641, System.IntPtr wparam, System.IntPtr lparam) Unknown
[Native to Managed Transition]
[Managed to Native Transition]
System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason = -1, int pvLoopData = 0) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = -1, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.ApplicationContext}) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown
System.Windows.Forms.dll!System.Windows.Forms.Application.Run(System.Windows.Forms.Form mainForm) Unknown
MyApp.exe!MyApp.Program.Main() Line 25 C#
有没有办法确保ResetBag
将与正确的ExecutionContext
一起运行**?**作为一个附带说明,AsyncLocal.Value
的实现在这里。
应用程序是.NET Framework 4.8,Windows窗体。
1条答案
按热度按时间gmxoilav1#
看起来你试图以一种不被设计的方式使用
AsyncLocal<T>
。异步本地值在调用堆栈中“向下”流动,而不是“向上”流动。理想情况下,它们是不可变的,这迫使代码设置Value
,而不是依赖于副作用(这在async local中很难考虑)。听起来你想要的更像是快照语义;我会研究System.Collections.Immutable(听起来像
ImmutableDictionary
会很好地为你服务),让每个任务锁定并复制当前值(如果需要的话,延迟创建),然后使用它的本地副本;让你的空闲处理程序锁定并复制当前值,并将其设置为null
。