在我的C#Silverlight应用程序中初始化UI时,我会对不同的服务进行几次异步调用。虽然异步执行所有加载非常好并且速度很快,但仍有时候我需要在最后发生加载的一些步骤。
在过去的一些观点中,我实施了一个“加载列表”机制,以帮助我跟踪加载,并保证在他们开火时任何行动的顺序。这是一个非常简单的例子:
private List<string> _loadingList = new List<string>();
// Called to begin the loading process
public void LoadData(List<long> IDs){
foreach(long id in IDs){
DoSomethingToLoadTheID(id);
_loadingList.Add(id.ToString());
}
}
// Called every time an ID finishes loading
public void LoadTheIDCompleted(object sender, ServiceArgs e){
UseTheLoadedData(e);
_loadingList.Remove(id.ToString());
if(_loadingList.Count == 0)
LoadDataFinally();
}
// Must be called after all other loading is completed
public void LoadDataFinally(){
ImportantFinishingTouches();
}
这件事适合我的目的,我还没有遇到任何问题。但是我对我对线程安全的了解并不像我想的那样自信,所以我想问几个问题:
(我使用的是Visual Studio 2013和.NET Framework 4.5.51209,以及Silverlight 5.0)
答案 0 :(得分:1)
这种事情有什么办法可以造成灾难性的后果吗?
完全取决于你在做什么。首先,您从多个线程访问List(of T)
。您应该使用ConcurrentList<T>
,因为它是线程安全的。请记住,多个线程访问/修改的任何类(包括.NET框架组件)必须是线程安全的。 MSDN指示哪些框架组件是和不是。
有没有更好的方法来实现同样的功能?
我没有在你的代码中看到使用多个线程的任何地方,但我认为你要做的是:
根据您对每个ID所做的事情的性质,方法可能会有所不同。例如,如果工作受CPU限制,那么您可以使用Tasks / TPL:
// Using TPL
var ids = new List<int>() {1, 2, 3, 4};
Parallel.ForEach(ids, id => DoSomething(id)); // Invokes DoSomething on each ID in the list in parallel
或者,如果您需要对事物执行的顺序进行细粒度控制......
var ids = new List<int>() {1, 2, 3, 4};
TaskFactory factory = new TaskFactory();
List<Task> tasks = new List<Task>();
foreach (var id in ids)
{
tasks.Add(factory.StartNew(() => DoSomething(id))); // executes async and keeps track of the task in the list
}
Task.WaitAll(tasks.ToArray()); // waits till everything is done
tasks.Clear();
foreach (var id in ids)
{
tasks.Add(factory.StartNew(() => DoSomethingElse(id))); // executes async and keeps track of the task in the list
}
Task.WaitAll(tasks.ToArray()); // Wait till all DoSomethingElse is done
// etc.
现在,如果您正在进行的工作是IO绑定(例如,在必须等待缓慢响应的情况下进行Web服务调用),您应该查看Asynchronous Programming with async and await (C#)。
有很多方法可以处理多线程,有时候找出正确的方法是挑战的一部分(异步/等待与任务对比信号量与背景工作者对等等。)