我在模板化控件中的线程同步时遇到一些问题(尝试执行自动完成控件)
在我的控制中,我有以下代码:
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
var searchTextBox = GetTemplateChild("SearchTextBox") as TextBox;
if (searchTextBox != null)
{
var searchDelegate = SearchAsync;
Observable.FromEventPattern(searchTextBox, "TextChanged")
.Select(keyup => searchTextBox.Text)
.Where(TextIsLongEnough)
.Throttle(TimeSpan.FromMilliseconds(500))
.Do(ShowProgressBar)
.SelectMany(searchDelegate)
.ObserveOn(Dispatcher)
.Subscribe(async results => await RunOnDispatcher(() =>
{
IsInProgress = false;
SearchResults.Clear();
foreach (var result in results)
{
SearchResults.Add(result);
}
}));
}
}
它抱怨在我的ShowProgressBar方法中,我试图访问由另一个线程编组的代码。
如果我注释掉油门和 ObserveOn(调度程序),它工作得很好,但它不会像我想要的那样限制我的服务调用。
如果我只注释掉油门部分,则什么都不会发生。
Asti的想法是正确的,但更好的方法是为Throttle提供IScheduler
参数:
// NB: Too lazy to look up real name
.Throttle(TimeSpan.FromMilliseconds(500), CoreDispatcherScheduler.Instance)
这将使下面的操作在UI线程(包括ShowProgressBar)上发生,直到SelectMany。
每个依赖项对象都要求仅在调度程序线程上对依赖项属性进行任何更改。Throttle
使用的是不同的计划程序,因此在后续Do
组合器中对 UI 进行任何更改都会导致访问异常。
您可以通过以下方式解决此问题:
- 在对调度程序造成副作用的任何操作之前添加 ObserveOnDispatcher。(可选)在管道中使用另一个计划程序。
- 使用 Dispatcher.Invoke 通过调度程序执行副作用。例如,
.Do(() => Dispatcher.Invoke(new Action(ShowProgressBar)))