我正在创建一个ViewModel对象的ObservableCollection,其中每个对象在初始化时都有几个要完成的任务。
我在父视图模型中将这些添加到像这样的ObservableCollection中:
public async void ButtonPressCommandHandler()
{
for (int i = 0; i < 25; ++i)
{
var testViewModel = new TestViewModel();
await testViewModel.Initialize();
TestViewModels.Add(testViewModel);
}
}
只需按一下按钮或其他一些事件即可调用该循环。
这是测试视图模型代码:
public class TestViewModel : ViewModelBase
{
private string _taskOne;
public string TaskOne
{
[DebuggerStepThrough]
get { return _taskOne; }
set
{
if (value != _taskOne)
{
_taskOne = value;
RaisePropertyChanged(() => TaskOne);
}
}
}
private string _taskTwo;
public string TaskTwo
{
[DebuggerStepThrough]
get { return _taskTwo; }
set
{
if (value != _taskTwo)
{
_taskTwo = value;
RaisePropertyChanged(() => TaskTwo);
}
}
}
public async Task Initialize()
{
TaskOne = await TaskOneAsync();
TaskTwo = await TaskTwoAsync();
}
private Task<string> TaskOneAsync()
{
return Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
return "Task one";
});
}
private Task<string> TaskTwoAsync()
{
return Task.Factory.StartNew(() =>
{
var random = new Random();
int randomNumber = random.Next(500, 5000);
Thread.Sleep(randomNumber);
return "Task two: " + randomNumber;
Thread.Sleep(randomNumber);
return "Task two: " + randomNumber;
});
}
}
(我知道我可以在构造函数中调用init工作,但这更接近我真正需要做的事情。)
在我看来,我有ListView
,ListView.ItemTemplate
只显示包含TaskOne
和TaskTwo
属性的文本块。它是ItemSource
绑定到TestViewModels
。
我看到的是,对于创建的25个对象中的每一个,TaskOne
和TaskTwo
属性都同时出现,并且每个对象仅在两个任务完成后显示。
如果我从await
删除Initialize()
并让Initialize()
返回void(这是UI线程上的IS),行为会更好 - 我看到所有TaskOne
属性都非常快速,然后TaskTwo
属性开始填写。但它们显示的随机值是错误的 - 有许多重复,它们似乎以块或4或5填充列表(很难说) 。
非测试代码中的完整目标是更新与TaskTwo
绑定的进度指示器。这样的事情(来自TestViewModel
代码):
public async Task Initialize()
{
TaskOne = await TaskOneAsync();
Loading = true;
TaskTwo = await TaskTwoAsync();
Loading = false;
}
所有对象立即加载到列表中,然后在完成时异步更新其TaskOne
和TaskTwo
属性,并根据TaskTwo
中的工作更新进度指示器。但到目前为止,没有运气让它发挥作用。
编辑:添加了更好的示例代码和解释
答案 0 :(得分:0)
首先,编写代码的方式结果应该如你所描述的那样。
我看到的是,对于创建的25个对象中的每一个,TaskOne和TaskTwo属性都同时出现,并且只有在两个任务完成后才显示e sadfach对象。
行await testViewModel.Initialize();
等待Initialize
方法,后者依次awaits
执行这两项任务。这意味着只有在两个任务都已完成后才添加项目(TestViewModels.Add(testViewModel);
)。
其次,如果您没有await
Initialize
,您可以在上一次运行完成之前再次调用它,这意味着您可以并行运行2个任务(使用StartNew
) 。这将导致几个Random实例彼此非常接近地创建。 Random类使用基于时间的种子来生成值,在这种情况下,种子将是相同的,因此相同的“随机”结果。来自随机MSDN文章:
默认种子值源自系统时钟并具有有限的分辨率。因此,通过调用默认构造函数紧密连续创建的不同Random对象将具有相同的默认种子值,因此将生成相同的随机数集。使用单个Random对象生成所有随机数可以避免此问题。您还可以通过修改系统时钟返回的种子值,然后将此新种子值显式提供给Random(Int32)构造函数来解决此问题
最后,我不鼓励在Task.Factory.StartNew
环境中同时使用Thread.Sleep
和async-await
。您可能应该使用Task.Run
和await Task.Delay
代替(我知道这是一个示例代码)。