前几天,在代码审查中,我看到了一些关于从工作线程设置属性的安全性的问题。一位同事发表了评论,“此属性绑定到UI,数据绑定是线程安全的。”
我一直在使用XF,但总是假设这不是真的:如果我想更新绑定到UI的ViewModel属性,我必须在UI线程上进行这些更改。我只花了几分钟探索文档,除了直接操作BindableProperties具有线程关联性之外,我没有看到明显的答案。如果这是阻止UI线程执行,那对我来说也很重要。
我可以制作一个项目来自己测试,但这些问题似乎总是间歇性的,所以我想知道我只是错过了正确的文档。
(这与调查我们看到的主要是在Xamarin调用堆栈中发生的崩溃有关。我们从工作线程引发事件,VM处理该事件并更新一些属性。如果这是为UI线程安排的,可能会有一些我们没有准备的交错问题,而且我更接近于解决崩溃问题。如果它没有安排用于UI线程......我很惊讶它的工作时间很长。)
答案 0 :(得分:1)
你是对的。 100%正确。您可以使用我一直在使用XF,但总是假设这不是真的:如果我想更新绑定到UI的ViewModel属性,我必须在UI线程上进行这些更改。
Device.InvokeOnMainThread()
。
要完成,有一种情况无关紧要:PropertyChanged
https://github.com/xamarin/Xamarin.Forms/blob/b645064/Xamarin.Forms.Core/BindingExpression.cs#L542
在这种情况下,Binding
应用于主(UI)线程。
答案 1 :(得分:1)
<强>假设强>
我们通过绑定专门与UI进行交互。所以只有制定者和吸气者在玩。
<强>吸气剂强>
@Stephane在github中引用的代码,仅表示将在UI线程中调用绑定的getter。不多也不少。
顺便说一句,我不会依赖源代码(我的意思是:实现),而是规范的重要性。实现可以随时改变,如果规范不需要这个,那么单元测试就不会检查这个,所以会有一个很大的惊喜...<强>塞特斯强>
但是仍然不清楚,你是否可以在后台线程中调用你的setter。无论答案是肯定还是否定,它都意味着需要解决的其他任务。
a)如果你可以在任何线程中调用setter:这意味着你的viewmodel内部数据暴露于竞争条件,因为将从UI线程调用,你将访问它们后台线程。结论:您必须使用通常的并发模式保护数据,或使用线程安全类,另外:您的getter和setter必须是原子的。
b)如果事实证明你无法在任何线程中调用setter:那么你必须使用Device.InvokeOnMainThread()
。但这只是故事的一半。在后台线程的方法中,您正在修改一个List实例。这将由getter在UI线程中访问,并且当您说填充它时,您将在后台线程中同时修改它。从理论上讲,用户可以在人口期间与UI进行交互,可以对绑定进行评估,调用getter。
<强>结论:强>
如果有多个线程访问同一数据,则必须始终锁定数据。
无论当前源代码意味着将getter和setter编组到UI线程的最安全方法。这不会导致显着的开销:如果执行已经在UI线程中,则马歇尔将不执行任何操作。
注意:即使您将getter和setter都编组到UI线程中,您也将访问后台线程中的数据,因此防范竞争条件或使用线程安全类是必不可少的。