将 AsyncLocal 流向 WinUI 中的 UI 线程 3
Flowing AsyncLocal to UI thread in WinUI 3
我正在使用 MainWindow 的 DispatcherQueue
将工作分派给 UI 线程。但是当我这样做时,我丢失了存储在 AsyncLocal
中的值。它似乎不会自动流动执行上下文,但我也担心如果我手动流动它会发生什么,因为那样也会流动同步上下文、区域设置信息等。
所以我的问题是,确保 AsyncLocal
变量流向 UI 线程的安全方法是什么?
我想从事的工作示例:
var asyncLocal = new AsyncLocal<int>();
asyncLocal.Value = 10;
var dispatcherQueue = _mainWindow.DispatcherQueue;
var taskCompletionSource = new TaskCompletionSource();
dispatcherQueue.TryEnqueue(new DispatcherQueueHandler(() =>
{
Debug.Assert(asyncLocal.Value == 10);
asyncLocal.Value = 20;
Debug.Assert(asyncLocal.Value == 20);
taskCompletionSource.SetResult();
}));
await taskCompletionSource.Task;
Debug.Assert(asyncLocal.Value == 10);
我还注意到堆栈跟踪丢失了,因为执行上下文没有流动,如果能够解决这个问题也很好。
简短的回答是你不能。您不能,因为将操作排队到 DispatcherQueue
不是异步流程的一部分。它基本上向 UI 线程发送消息以执行方法。
您需要在线程之间共享数据 - 异步执行线程和 UI 线程。如果你只有一个异步流,即你的异步代码不是可重入的,也不是从多个线程启动的,那么任何全局存储都可以。
如果您的代码是可重入的或可以从多个线程/任务启动,那么您需要一个可以识别其调用上下文的全局存储。例如,您可以使用 ConcurrentDictionary<Guid, MyStorageClass>
.
那么你可以这样做:
var localStorage = new MyStorageClass(); //Init accordingly
localStorage.Value = 10; //Assuming your class has a property called Value
var sessionGuid = Guid.NewGuid();
concurrentStorage.TryAdd (sessionGuid, localStorage);
var dispatcherQueue = _mainWindow.DispatcherQueue;
var taskCompletionSource = new TaskCompletionSource();
dispatcherQueue.TryEnqueue(new DispatcherQueueHandler(() =>
{
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 10);
concurrentStorage[sessionGuid].Value.Value = 20;
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 20);
taskCompletionSource.SetResult();
}));
await taskCompletionSource.Task;
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 10);
我正在使用 MainWindow 的 DispatcherQueue
将工作分派给 UI 线程。但是当我这样做时,我丢失了存储在 AsyncLocal
中的值。它似乎不会自动流动执行上下文,但我也担心如果我手动流动它会发生什么,因为那样也会流动同步上下文、区域设置信息等。
所以我的问题是,确保 AsyncLocal
变量流向 UI 线程的安全方法是什么?
我想从事的工作示例:
var asyncLocal = new AsyncLocal<int>();
asyncLocal.Value = 10;
var dispatcherQueue = _mainWindow.DispatcherQueue;
var taskCompletionSource = new TaskCompletionSource();
dispatcherQueue.TryEnqueue(new DispatcherQueueHandler(() =>
{
Debug.Assert(asyncLocal.Value == 10);
asyncLocal.Value = 20;
Debug.Assert(asyncLocal.Value == 20);
taskCompletionSource.SetResult();
}));
await taskCompletionSource.Task;
Debug.Assert(asyncLocal.Value == 10);
我还注意到堆栈跟踪丢失了,因为执行上下文没有流动,如果能够解决这个问题也很好。
简短的回答是你不能。您不能,因为将操作排队到 DispatcherQueue
不是异步流程的一部分。它基本上向 UI 线程发送消息以执行方法。
您需要在线程之间共享数据 - 异步执行线程和 UI 线程。如果你只有一个异步流,即你的异步代码不是可重入的,也不是从多个线程启动的,那么任何全局存储都可以。
如果您的代码是可重入的或可以从多个线程/任务启动,那么您需要一个可以识别其调用上下文的全局存储。例如,您可以使用 ConcurrentDictionary<Guid, MyStorageClass>
.
那么你可以这样做:
var localStorage = new MyStorageClass(); //Init accordingly
localStorage.Value = 10; //Assuming your class has a property called Value
var sessionGuid = Guid.NewGuid();
concurrentStorage.TryAdd (sessionGuid, localStorage);
var dispatcherQueue = _mainWindow.DispatcherQueue;
var taskCompletionSource = new TaskCompletionSource();
dispatcherQueue.TryEnqueue(new DispatcherQueueHandler(() =>
{
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 10);
concurrentStorage[sessionGuid].Value.Value = 20;
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 20);
taskCompletionSource.SetResult();
}));
await taskCompletionSource.Task;
Debug.Assert(concurrentStorage[sessionGuid].Value.Value == 10);