2017-10-16 141 views
1

Tpl Sample Click here to downloadTPL实现问题

嗨, 我想实现我的项目中第三方物流。我使用Web客户端创建了5个并行HTTP调用。

我想实现的是什么。 如果5个并行呼叫中的任何一个返回包含“First”的字符串,则清除其余呼叫并继续呼叫返回“First”。

我曾尝试:

我已附加上述示例代码。在那里我使用了Predicate函数。

async Task<T> WhenAny<T>(IEnumerable<Task<T>> tasks, Func<T, bool> predicate) 
    { 
     var taskList = tasks.ToList(); 
     Task<T> completedTask = null; 
     do 
     { 
      completedTask = await Task.WhenAny(taskList); 
      taskList.Remove(completedTask); 
     } while (!predicate(await completedTask) && taskList.Any()); 

     return completedTask == null ? default(T) : await completedTask; 
    } 

,并呼吁该如下:

public async Task<string> methodname() 
    { 
     string sUrl = "https://abcd.com/test.php"; 
     Task<string> task1 = DownLoadData(sUrl); 
     Task<string> task2 = DownLoadData(sUrl); 
     Task<string> task3 = DownLoadData(sUrl); 
     Task<string> task4 = DownLoadData(sUrl); 
     Task<string> task5 = DownLoadData(sUrl); 
     var tasks = new[] { task1, task2, task3, task4, task5 }; 
     await WhenAny(tasks, t => t.Contains("First")); 

     return ""; 


    } 

但它并不满足标准。请建议我错过了什么。任何帮助,将不胜感激。

+2

为什么重新创建Task.WhenAny?你是什​​么意思“杀死休息”?你不能告诉* HTTP服务器*停止处理,除非服务器本身提供这样的API –

+0

@PanagiotisKanavos他们没有。 – Servy

+0

@Servy现在我明白了。在这种情况下,不幸的方法名称 –

回答

2

我假设你有一个网址列表检查,而不是反复检查相同的URL(?!),但是同样的方法可以在任何情况下....

我喜欢用TPL Dataflow的使用这种事情。它允许您通过多个同步操作(如检查子字符串“First”的内容)将多个异步操作(例如下载字符串)链接在一起,并提供所有方式的“旋钮”来调整并行度,缓冲区大小等的控制程度。我们还可以通过取消令牌取消进一步的处理。因此,首先让我们创建一个取消标记来源:

var cancellationSource = new CancellationTokenSource(); 

现在,我们的第一个“块”将是一个TransformBlock<string,string>。这是该区块的作业采取URL字符串和“改造”成一个内容串致电DownloadData功能:

var downloadBlock = new TransformBlock<string,string>(
     async s => await DownloadData(s), 
     new ExecutionDataflowBlockOptions 
     { 
      MaxDegreeOfParallelism = 10, //allow up to 10 simulteneous downloads 
      BoundedCapacity = 100,  //allow the block to store up to 100 urls in a buffer 
      CancellationToken = cancellationSource.Token 
     }); 

第二块是一个TransformMany<string,string>块。这种类型的块允许我们将字符串转换为字符串集合。我们只是给这个过滤掉哪些不通过返回一个空的集合符合我们标准的任意内容字符串的工作:

var filterBlock = new TransformManyBlock<string,string>(
     s => { 
       if (s.Contains("First")) 
       { 
        return new string[]{s}; 
       } else 
       { 
        return new string[]{}; 
       } 
      }); // (we could also pass a cancellation token here) 

最后一块是你会做你已经找到了内容字符串的东西(即第一下载的内容包含“First”)。我们也Cancel()我们取消标记源,以阻止我们在开始任何新的URL处理,一旦我们找到了一个会议我们的准则:

var finalBlock = new ActionBlock<string>(
     s => { 
       cancellationSource.Cancel(); 
       //do whatever you want with the content string here    
      }); 

所有这一切,我们现在需要做的是加入了块,并在网址的饲料:

downloadBlock.LinkTo(
    filterBlock, 
    new DataflowLinkOptions 
    { 
     PropagateCompletion = true // this flag ensures that if the first block completes ALL urls it signals to the nect block that no more will come 
    } 
); 
filterBlock.LinkTo(
    finalBlock, 
    new DataflowLinkOptions 
    { 
     PropagateCompletion = true, 
     MaxMessages = 1   //we are only interested in the first message that arrives in our final block 
    }); 

downloadBlock.Post("http://url.com/blah1"); 
downloadBlock.Post("http://url.com/blah2"); 
// etc... 

downloadBlock.Complete(); // this signals that we've ran out of Urls to process 
finalBlock.Completion.Wait(); //now wait for any messages we have 'in progress' to work through the pipeline 
+0

感谢Stewart_R我将尝试使用此解决方案并尽快更新。之后,我会将此标记为答案。 –