Если я могу предложить использовать Reactive Extensions (Rx) поверх TPL, то это можно сделать довольно легко без необходимости отмены задач и т. Д.
Если я могу предположить, что у вас есть следующее:
// The initial Uri
Uri initialUri = ...;
// A function to return the JSON string from a given Uri
Func<Uri, string> getJason = ...;
// Turn the JSON into the next set of Uris to fetch
Func<string, IEnumerable<Uri>> getUris = ...;
Затем, используя Rx, вы превращаете эти функции в функции, которые возвращают наблюдаемые с помощью пула задач, например:
Func<Uri, IObservable<string>> getJasonObsFunc = u =>
Observable
.FromAsyncPattern<Uri, string>(
getJason.BeginInvoke,
getJason.EndInvoke)
.Invoke(u)
.ObserveOn(Scheduler.TaskPool);
Func<string, IObservable<Uri>> getUrisObsFunc = j =>
Observable
.FromAsyncPattern<string, IEnumerable<Uri>>(
getUris.BeginInvoke,
getUris.EndInvoke)
.Invoke(j)
.ObserveOn(Scheduler.TaskPool)
.SelectMany(xs => xs.ToObservable());
Вам понадобится обратный вызов, чтобы получить пары Uri / JSON. Примерно так:
Action<Uri, string> callback = (u, j) =>
Console.WriteLine(String.Format("{0} => {1}", u, j));
Вот рекурсивный запрос LINQ, который будет рекурсивно извлекать каждую строку JSON:
Func<Uri, IObservable<Uri>> getAllUris = null;
getAllUris = u =>
Observable
.Return<Uri>(u)
.Merge(
from j in getJasonObsFunc(u).Do(k => callback(u, k))
from u1 in getUrisObsFunc(j)
from u2 in getAllUris(u1)
select u2);
Затем вы вызываете все это добро, используя следующую строку:
var subscription = getAllUris(initialUri).Subscribe();
Теперь, если вы хотите отменить выполнение запроса, просто вызовите это:
subscription.Dispose();
Rx обрабатывает все задачи и отменяет их все для вас.
Надеюсь, это поможет.
Вот ссылки для Rx: