C#:异步运行此代码的不同方法?

时间:2011-02-14 17:26:39

标签: c# .net vb.net multithreading

我有这段代码

List<string> myList = new List<string>();

myList.AddRange(new MyClass1().Load());
myList.AddRange(new MyClass2().Load());
myList.AddRange(new MyClass3().Load());

myList.DoSomethingWithValues();

异步运行任意数量的Load()方法的最佳方法是什么,然后确保在所有异步线程完成时运行DoSomethingWithValues()(当然每次回调发生时都不会增加变量并等待== 3)

4 个答案:

答案 0 :(得分:8)

我个人最喜欢的是:

List<string> myList = new List<string>();

var task1 = Task.Factory.StartNew( () => new MyClass1().Load() );
var task2 = Task.Factory.StartNew( () => new MyClass2().Load() );
var task3 = Task.Factory.StartNew( () => new MyClass3().Load() );

myList.AddRange(task1.Result);
myList.AddRange(task2.Result);
myList.AddRange(task3.Result);

myList.DoSomethingWithValues();

答案 1 :(得分:5)

PLINQ怎么样?

var loadables = new ILoadable[] 
                { new MyClass1(), new MyClass2(), new MyClass3() };

var loadResults = loadables.AsParallel()
                           .SelectMany(l => l.Load());

myList.AddRange(loadResults);

myList.DoSomethingWithValues();

编辑:如Reed Copsey所指出的,更改选择为SelectMany。

答案 2 :(得分:1)

Ani的概念解决方案可以更简洁地编写:

new ILoadable[] { new MyClass1(), new MyClass2(), new MyClass3() }
    .AsParallel().SelectMany(o => o.Load()).ToList()
    .DoSomethingWithValues();

这是我首选的解决方案:声明式(AsParallel)和简洁。

当以这种方式书写时,里德的解决方案如下:

new ILoadable[] { new MyClass1(), new MyClass2(), new MyClass3() }
    .Select(o=>Task.Factory.StartNew(()=>o.Load().ToArray())).ToArray()
    .SelectMany(t=>t.Result).ToList()
    .DoSomethingWithValues();

请注意,两个 ToArray调用可能都是必要的。如果o.Load是惰性的(通常它可以是YMMV),则第一次调用是必要的,以确保在后台任务中完成对o.Load的评估。第二次调用是必要的,以确保在调用SelectMany之前已完全构造任务列表 - 如果您不这样做,那么SelectMany将尝试仅在必要时迭代其源 - 即它不会在它必须之前迭代到第二个任务,并且直到第一个任务的Result被计算出来。实际上,您正在启动任务,但随后又一个接一个地执行它们 - 将后台任务重新转换为严格的顺序执行。

请注意,第二个声明性较小的解决方案存在更多陷阱,并且需要进行更彻底的分析以确保它是正确的 - 即,这不太可维护,尽管仍然比手动线程更好。顺便提一下,您可以放弃拨打.ToList的电话 - 这取决于DoSomethingWithValues的详细信息 - 以获得更好的性能,即您的最终处理可以访问第一个值,因为它们涓涓细流无需等待所有任务或并行枚举完成。而且启动时间更短!

答案 3 :(得分:-1)

除非有令人信服的理由尝试一次性运行它们,否则我建议您只使用一个异步方法运行它们。

令人信服的原因可能是重型磁盘/数据库IO意味着运行多个后台线程实际上允许它们同时运行。如果大多数初始化实际上是代码逻辑,您可能会发现多个线程实际上导致性能降低。