如何在线程安全的方法中调用 Dispatcher.RunAsync() 来更新 UI 组件 [英] How to call Dispatcher.RunAsync() in a thread safe method to update UI components
问题描述
首先,经过深思熟虑,我认为我的标题不正确.
但是,我一直试图找到一种方法来更新从 Task.Run(async ()=>{ }) 表达式/方法产生的多个线程的 UI 控件.我正在 UWP windows mobile 10 应用程序中执行此操作.
我已经列出了大部分涉及的例程,需要帮助来完成对 DisplayProgress(string message) 方法的调用.在此方法中,我想使用 Dispathcer.RunAsync() 以线程安全的方式更新 UI 控件,但需要它是线程安全的.请参阅下面代码中的尝试 1&2.
公共密封部分类 SynchProcess : BasePage{protected override void OnNavigatedTo(NavigationEventArgs e){Task.Run(async()=>{var 结果 = await SynchTables();});}私有异步任务同步表(){布尔 bRet = 真;列表<任务>任务 = 新列表<任务>();尝试{//刷新 1tasks.Add(Task.Run(async () =>{//更新界面DisplayProgress(汽车刷新……正在运行");列表<汽车>汽车 = 等待 _dataService.GetCarsData();DataAccessSQLite.DeleteAll();DataAccessSQLite.InsertCars(cars);//更新界面DisplayProgress(汽车刷新……完成");}));//刷新 2tasks.Add(Task.Run(async () =>{//更新界面DisplayProgress("Tracks refresh…running");列表<曲目>轨道 = 等待 _dataService.GeTrackstData();DataAccessSQLite.DeleteAll
我发现这篇文章 http://briandunnington.github.io/uitask.html,但是,我不确定这种方法是否正确或线程安全.似乎有很多样板代码应该由 Dispatacher.RunAsync() 处理?我读过的其他文章提到了 Action 委托,我认为它应该是线程安全的,但是在写了几篇(非常详细的技术文章)之后,我现在很困惑.附加文章:C# 委托线程安全吗?(不幸的是,我只能链接到 2 篇文章 - 需要先增加我的积分!)
我不确定这种方法是否正确或线程安全.似乎有很多样板代码应该由 Dispatacher.RunAsync() 处理?
您可以通过使用 UI 元素的 Dispatcher 属性或存在于 UI 线程上下文中的任何对象的 Dispatcher 属性安全地访问 UI 元素(例如按钮所在的页面).
在 UI 线程上创建的 Windows 运行时对象的生命周期受线程生命周期的限制.不要在窗口关闭后尝试访问 UI 线程上的对象.
以下是关于 Microsoft github 存储库.
<块引用>通常,您可以通过调用 Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>/* update the UI */) 从后台线程更新 UI.然而,这只是在 UI 线程上安排工作并立即返回,即使任务是等待用户在弹出框中输入也是如此.它也不提供任务将结果返回给调用者的方法.
RunTaskAsync 提供了一种替代方案,它结合使用 TaskCompletionSource 和 RunAsync 来返回一个您可以从后台线程等待的任务,从而暂停执行,直到 UI 任务完成.
因为 RunTaskAsync 是一个扩展方法,你把它当作一个 Dispatcher 上的方法来调用: var result = await Dispatcher.RunTaskAsync(async () => { ...; return value; });
使用系统;使用 System.Threading.Tasks;使用 Windows.UI.Core;公共静态类 DispatcherTaskExtensions{公共静态异步任务<T>RunTaskAsync(这个 CoreDispatcher 调度程序,Func<Task<T>>func, CoreDispatcherPriority 优先级 = CoreDispatcherPriority.Normal){var taskCompletionSource = new TaskCompletionSource();await dispatcher.RunAsync(priority, async() =>{尝试{taskCompletionSource.SetResult(await func());}捕获(异常前){taskCompletionSource.SetException(ex);}});返回等待 taskCompletionSource.Task;}//没有 TaskCompletionSource所以我们使用我们扔掉的布尔值.公共静态异步任务 RunTaskAsync(这个 CoreDispatcher 调度程序,功能<任务>func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);}
<块引用>
是否有必要锁定多个线程通过调度程序访问同一个 uwp 组件的代码片段?
您不必添加锁.Dispatcher.RunTaskAsync 请求不会在其他代码中间运行(这就是它们的全部意义).
First, I don’t think I have titled this correctly – after much thought.
However, I have been trying to find a way to update a UI control from multiple threads spawned from a Task.Run(async ()=>{ }) expression/method. I’m doing this in an UWP windows mobile 10 application.
I've listed majority of the routines involved, and need help with completing the call to the DisplayProgress(string message) method. In this method, I want to update the UI control in a thread safe manner using Dispathcer.RunAsync(), but need it to be thread safe. Please see Attempt 1&2 in the code below.
public sealed partial class SynchProcess : BasePage
{
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Task.Run(async ()=>{
var result = await SynchTables();
});
}
private async Task<bool> SynchTables()
{
Bool bRet = true;
List<Task> tasks = new List<Task>();
Try
{
// Refresh 1
tasks.Add(Task.Run(async () =>
{
// Update UI
DisplayProgress("Cars refresh…running");
List<Car> cars = await _dataService.GetCarsData();
DataAccessSQLite.DeleteAll<Car>();
DataAccessSQLite.InsertCars(cars);
// Update UI
DisplayProgress("Cars refresh…completed");
}));
// Refresh 2
tasks.Add(Task.Run(async () =>
{
//Update UI
DisplayProgress("Tracks refresh…running");
List<Track> tracks = await _dataService.GeTrackstData();
DataAccessSQLite.DeleteAll<Track>();
DataAccessSQLite.InsertTracks(tracks);
//Update UI
DisplayProgress("Tracks refresh…completed");
}));
// Refresh 3
// Refresh 4
…
…
Task.WaitAll(tasks.ToArray());
}
Catch(AggreggateException agEx)
{
bRet = false;
// removed for brevity
…
}
return bRet;
}
// Attempt 1 : This i was aware of, but included for completeness.
// FAIL - The await operator can only be used in an async method.
private static void DisplayProgress(string message)
{
lock(_lockObject)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//Update control logic;
});
}
// Attempt 2
// FAIL - An object reference is required for non-static field, method or
// property ‘DependencyObject.Dispatcher’
private static async Task DisplayProgress (string message)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Update UI logic here
});
}
}
I found this article http://briandunnington.github.io/uitask.html, however, I’m not sure if this approach is correct or thread safe. Seems like a lot of boilerplate code for something that should be handled by Dispatacher.RunAsync()? Additional articles I’ve read pointed to Action delegates, which I think is supposed to be thread safe, but after several (very detailed technical articles) I am now confused. Additional articles: Are C# delegates thread-safe? (unfortunately, I can only link to 2 articles - need to increase my points first !)
I’m not sure if this approach is correct or thread safe. Seems like a lot of boilerplate code for something that should be handled by Dispatacher.RunAsync()?
You can access the UI element safely by using its Dispatcher property, or the Dispatcher property of any object that exists in the context of the UI thread (such as the page the button is on).
The lifetime of a Windows Runtime object that is created on the UI thread is bounded by the lifetime of the thread. Do not try to access objects on a UI thread after the window has closed.
The following is official suggestion on a Microsoft github repository.
Normally, you can update your UI from a background thread by calling Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => /* update the UI */). However, this merely schedules the work on the UI thread and returns immediately, even if the task is to await user input in a popup box. It also does not provide a way for the task to return a result to the caller.
RunTaskAsync provides an alternative that uses TaskCompletionSource in combination with RunAsync to return a Task that you can await from your background thread, thereby pausing execution until the UI task completes.
Because RunTaskAsync is an extension method, you call it as if it were a method on Dispatcher: var result = await Dispatcher.RunTaskAsync(async () => { ...; return value; });
using System;
using System.Threading.Tasks;
using Windows.UI.Core;
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher dispatcher,
Func<Task<T>> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
{
var taskCompletionSource = new TaskCompletionSource<T>();
await dispatcher.RunAsync(priority, async () =>
{
try
{
taskCompletionSource.SetResult(await func());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
return await taskCompletionSource.Task;
}
// There is no TaskCompletionSource<void> so we use a bool that we throw away.
public static async Task RunTaskAsync(this CoreDispatcher dispatcher,
Func<Task> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
await RunTaskAsync(dispatcher, async () => { await func(); return false; }, priority);
}
Is it necessary to lock code snippet where multiple threads access same uwp component via dispatcher?
You don't have to add a lock. Dispatcher.RunTaskAsync requests will not run in the middle of other code (that's the whole point of them).
这篇关于如何在线程安全的方法中调用 Dispatcher.RunAsync() 来更新 UI 组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!