项目数未知时定义下一个起点

Define Next Start Point When Number of Items Unknown

我有一个需要查询的 Web 服务,它需要一个支持其数据分页的值。由于我需要获取的数据量以及该服务的实现方式,我打算执行一系列并发的 http 网络请求来积累这些数据。

假设我有线程数和页面大小,我如何分配每个线程来选择不与其他线程重叠的起点?自从我进行并行编程以来已经有很长一段时间了,而且我有点挣扎。我知道我可以用 start = N/numThreads * threadNum 之类的东西找到我的起点,但我不知道 N。现在我只是启动 X 个线程和每个循环,直到它们没有更多数据。问题是它们往往会重叠,我最终会得到重复的数据。我需要独特的数据而不是浪费请求。

现在我的代码看起来像这样。这是许多尝试之一,我明白为什么这是错误的,但最好展示一些东西。目标是从网络服务并行收集数据页面:

       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。

您是否试图通过发出多个并发请求来强制远程服务并行?分页通常用于将返回的数据量限制为仅需要的数据量,但是如果您需要所有数据,那么尝试先分页然后再重建它似乎是一个糟糕的设计。您的代码变得不必要地复杂,难以维护,您可能只是将瓶颈从您控制的代码转移到其他地方,现在您已经引入了数据完整性问题(如果所有这些线程访问不同版本的数据会发生什么正在尝试查询?)。通过增加调用的复杂性和数量,您也增加了发生问题的可能性(例如,其中一个连接断开)。

您能否说明您正试图解决的问题,这样或许我们可以帮助设计更好的解决方案?

除非您知道实际限制,否则我不确定是否有办法在不浪费一些请求的情况下执行此操作。下面的代码可能有助于消除重复数据,因为您只需在每个索引上查询一次:

    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 returns 什么都没有,您可以取消对 4 以上索引的所有查询,这些查询仍然处于活动状态。

或者,如果您可以对最大索引做出合理的猜测,您或许能够实施一种算法来在检索任何数据之前查明确切的限制。不过,如果您的猜测相当准确,这可能只会更有效率。