我有一个我需要查询的Web服务,它需要一个支持其数据分页的值。由于我需要获取的数据量以及如何实现该服务,我打算执行一系列并发的http Web请求来累积此数据。
假设我有线程数和页面大小我如何分配每个线程来选择与其他线程不重叠的起点?自从我进行并行编程以来,已经很长一段时间了,我有点挣扎。我知道我可以用start = N/numThreads * threadNum
之类的东西找到我的起点然而我不知道N.现在我只是旋转X个线程和每个循环,直到它们不再获得数据。问题是它们往往重叠,我最终得到重复的数据。我需要独特的数据而不是浪费请求。
现在我的代码看起来像这样。这是许多尝试之一,我明白为什么这是错误的,但更好地展示一些东西。目标是并行收集来自Web服务的数据页面:
int limit = pageSize;
data = new List<RequestStuff>();
List<Task> tasks = new List<Task>();
for (int i = 0; i < numThreads; i++)
{
tasks.Add(Task.Factory.StartNew(() =>
{
try
{
List<RequestStuff> someData;
do
{
int start;
lock(myLock)
{
start = data.Count;
}
someKeys = GetDataFromService(start, limit);
lock (myLock)
{
if (someData != null && someData.Count > 0)
{
data.AddRange(someData);
}
}
} while (hasData);
}
catch (AggregateException ex)
{
//Exception things
}
}));
}
Task.WaitAll(tasks.ToArray());
没有竞争条件的任何灵感来解决这个问题?如果重要的话,我需要坚持使用.NET 4。
答案 0 :(得分:1)
除非你知道实际的限制,否则我不确定是否有办法在不浪费一些请求的情况下这样做。下面的代码可能有助于消除重复数据,因为您只会在每个索引上查询一次:
private int _index = -1; // -1 so first request starts at 0
private bool _shouldContinue = true;
public IEnumerable<RequestStuff> GetAllData()
{
var tasks = new List<Task<RequestStuff>>();
while (_shouldContinue)
{
tasks.Add(new Task<RequestStuff>(() => GetDataFromService(GetNextIndex())));
}
Task.WaitAll(tasks.ToArray());
return tasks.Select(t => t.Result).ToList();
}
private RequestStuff GetDataFromService(int id)
{
// Get the data
// If there's no data returned set _shouldContinue to false
// return the RequestStuff;
}
private int GetNextIndex()
{
return Interlocked.Increment(ref _index);
}
还可以通过添加取消令牌来取消任何您知道浪费的索引来改进,即如果索引4没有返回任何内容,您可以取消对仍然有效的4以上索引的所有查询。
或者,如果您可以对最大索引进行合理猜测,则可以在检索任何数据之前实现算法以确定精确限制。如果您的猜测相当准确,这可能会更有效。
答案 1 :(得分:0)
您是否尝试通过发出多个并发请求来强制远程服务的并行性?分页通常用于将返回的数据量限制为仅需要的数据,但如果您需要所有数据,那么尝试首页然后重新构建它似乎是一种糟糕的设计。您的代码变得不必要地复杂,难以维护,您可能只是将您控制的代码的瓶颈移到其他地方,现在您已经引入了数据完整性问题(如果所有这些线程访问不同的版本会发生什么)您要查询的数据?)。通过增加调用的复杂性和数量,您也增加了发生问题的可能性(例如,其中一个连接被丢弃)。
您能否说明您试图解决的问题,或许我们可以帮助您构建更好的解决方案?