我试图同时调用许多不同的Web服务并聚合数据。
我的目的是为每个Web调用创建一个Task,将共享容器传递给每个任务,并将每个调用的数据存储在容器中。只要我可以从每个Web调用中获取数据到共享容器中,我很高兴。
我已经创建了一个我正在尝试做的示例 - 但是,它有时会在Task.WaitAll行上发生异常崩溃:“发生了一个或多个错误。(源数组不够长。检查源代码index,length和数组的下限。 参数名称:sourceArray)“。
我是使用async / await和多线程的新手。
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using System.Linq;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting tasks...");
List<Task> tasks = new List<Task>();
List<char> container = new List<char>();
for (int i = 0; i < 80; i++)
{
tasks.Add(LongTask(container));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Checkpoint 1.");
Console.WriteLine("Tasks Finished");
Console.ReadLine();
}
public static async Task<string> LongTask(List<char> container)
{
var client = new HttpClient();
var text = await client.GetAsync("http://www.google.com");
var myList = text.StatusCode.ToString().ToList();
container.AddRange(myList);
return text.StatusCode.ToString();
}
}
}
答案 0 :(得分:3)
在添加也可以使用的数据时,您可以在当前列表周围使用lock
构造。
将进入班级
private static object _lock = new object();
这将在您的方法中
var myList = text.StatusCode.ToString().ToList();
lock(_lock)
{
container.AddRange(myList);
}
或您可以使用ConcurrentBag
所以在你的代码中用main方法替换List,在main方法
中ConcurrentBag<string> container = new ConcurrentBag<string>();
并在LongTask中(方法如下所示。
var myList = text.StatusCode.ToString().ToList();
container.AddRange(myList);
这里我建议您将ConcurrentBag
作为AddRange
的字符串列表具有foreach
方法,如果您确实写ConcurrentBag
以添加char
,则可能是其他线程也可能在中间添加string
,而不是在完成所有内容后,您可以从ConcurrentBag
中取出ConcurrentBag<T>
并将其转换为字符数组或列表。
注意: SELECT * FROM...
是一个线程安全的包实现,针对同一个线程生成和使用存储在包中的数据的情况进行了优化。