Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# C:异步运行此代码的不同方式?_C#_.net_Vb.net_Multithreading - Fatal编程技术网

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

C# C:异步运行此代码的不同方式?,c#,.net,vb.net,multithreading,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(); 异步运行任意数量的加载方法,然后确保在所有异步线程完成时运行DoSo

我有这个密码

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

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

myList.DoSomethingWithValues();

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

我个人最喜欢的方法是:

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();
怎么样


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

除非有令人信服的理由尝试一次运行所有对象,否则我建议您只需在一个异步方法中运行所有对象


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

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将仅在必要时尝试迭代其源-即,它不会在必须之前迭代到第二个任务,直到计算完第一个任务的结果。实际上,您正在启动任务,但随后一个接一个地懒洋洋地执行这些任务—将后台任务变回严格的顺序执行


请注意,第二个声明性较少的解决方案有更多的缺陷,需要进行更彻底的分析以确保其正确性,即,这一解决方案的可维护性较差,但仍比手动线程好几英里。顺便说一句,您可以省去对.ToList的调用(这取决于DoSomethingWithValues的详细信息),从而获得更好的性能,这样您的最终处理就可以在第一个值慢慢进入时访问它们,而无需等待所有任务或并行枚举完成。而且开机时间更短

你现在从我这里得到+1;有一件事可能重要,也可能不重要——我喜欢这个选项,但它确实改变了myList中元素相对于原始元素的顺序。这在这个例程中可能很重要,也可能不重要,但值得注意的是,很少有称为Load的例程是100%CPU限制的,也很少有CPU是单核的。我敢打赌,在这种情况下,并行很少会导致性能降低。除非这几乎是纯磁盘IO限制,否则并行运行可能会显著提高性能。由于大多数DB服务器都能很好地处理并发性,因此并行操作对繁重的DB IO有很大的帮助。除非是单核系统,否则纯CPU限制的工作仍然会有很大帮助,在这种情况下,上面的TPL选项仍然可以很好地处理这种情况……正如我在回答中所说:大量使用磁盘/数据库是一个令人信服的原因,但命名方法负载并不总是意味着它在做任何繁重的工作。我的观点是OP没有指定方法在做什么,简单地假设线程化或并行化一组方法调用将提高性能永远不是正确的方法。警告:即使工作负载是微不足道的并行化,启动任务有相当大的开销,不值得进行琐碎的计算,而且很多计算都是琐碎的。假设您处理的方法需要毫秒或更长时间,那么这个任务是值得的,考虑看CTP下一个版本的C/VB:我们希望在论坛上得到用户反馈:如果负载被实现为懒惰地评估的可枚举,那么这可能不起作用。
new ILoadable[] { new MyClass1(), new MyClass2(), new MyClass3() }
    .Select(o=>Task.Factory.StartNew(()=>o.Load().ToArray())).ToArray()
    .SelectMany(t=>t.Result).ToList()
    .DoSomethingWithValues();