在我的Windows Phone 8.1应用程序中,我有一个单件服务DataService,它应该偶尔下载一些数据。同时在UI上我应该显示收到的数据量。当用户登录应用程序时,将调用DataService.StartGettingData():
void StartGettingData()
{
if (getDataTaskCancellationTokenSource != null)
getDataTaskCancellationTokenSource.Cancel();
getDataTaskCancellationTokenSource = new CancellationTokenSource();
var token = getDataTaskCancellationTokenSource.Token;
Task.Factory.StartNew(async () => await ExecuteCycleAsync(token), token);
}
async Task ExecuteCycleAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
await LoadDataAsync(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(timeTillNextDownload, cancellationToken);
}
}
当用户在
的帮助下退出时,此任务将被取消if (getDataTaskCancellationTokenSource != null)
getDataTaskCancellationTokenSource.Cancel();
包含下载结果的属性如下所示:
List<DataType> Data = new List<DataType>();
public IEnumerable<DataType> Data
{
get { return Data; }
set
{
Data = value.ToList();
OnDataUpdated();
}
}
void OnDataUpdated()
{
var handler = DataUpdated;
if (handler != null)
handler(this, EventArgs.Empty);
}
这部分似乎正常工作,直到我必须在屏幕上显示数据量。 我的MainViewModel获取了使用Ninject注入的DataService实例。
readonly IDataService DataService;
public MainViewModel(IDataService dataService)
{
DataService = dataService;
DataService.DataUpdated += DataService_DataUpdated;
UpdateDataCount();
}
void DataService_DataUpdated(object sender, EventArgs e)
{
UpdateDataCount();
}
void UpdateDataCount()
{
DataCount = DataService.Data.Count();
}
在xaml中,我将TextBlock绑定到MainViewModel的DataCount属性
int DataCount;
public int DataCount
{
get { return DataCount; }
set
{
DataCount = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
这就是出现问题的地方:OnPropertyChanged失败了 &#34;应用程序调用了为不同线程编组的接口。 (来自HRESULT的异常:0x8001010E(RPC_E_WRONG_THREAD))&#34;它被DataService.LoadDataAsync()捕获。 我理解运行时试图告诉我我从非ui线程访问UI元素。但是我呢?我认为OnPropertyChanged是将UI与其他后台任务断开连接的神奇之处。 当然,这个问题可以通过这种方式实现OnPropertyChanged来解决:
public CoreDispatcher Dispatcher { get; set; }
protected async void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
handler(this, new PropertyChangedEventArgs(propertyName));
});
}
}
但它应该以这种方式实施吗?或者我在DataService.ExecuteCycleAsync()?
中遗漏了一些东西答案 0 :(得分:7)
不要试图深入挖掘,我相信你的问题是:
Task.Factory.StartNew(async () => await ExecuteCycleAsync(token), token);
将其更改为简单,看看它是否按预期工作:
ExecuteCycleAsync(token);
否则,ExecuteCycleAsync
内的代码开始在没有同步上下文的线程上执行,这可能导致所有不同类型的问题,具体取决于LoadDataAsync
内的内容。
请注意,像这样调用ExecuteCycleAsync(token)
仍然是一个即发即弃的调用,可能无法观察到任何异常(更多here)。您可能希望存储它返回的Task
对象,以便以后能够观察它。