首页 > 解决方案 > TPL - 减少后期延迟

问题描述

我正在构建一个网络爬虫,似乎 TPL 非常适合我的用例。这是我想到的流程:

GetCommentsFromA --|                     |-- NotifyC
                   |-- ExtractKeywords --| 
GetCommentsFromB --|                     |-- NotifyD

本质上,存在三种类型的组件:收集器、转换器和通知器。收集者通过抓取不同的网站来收集评论,然后将评论传递给变形金刚。他们预计每 X 秒轮询一次网页。在这种情况下,唯一存在的转换器是负责从评论中提取关键字的转换器。他们本质上是永无止境的生产者。

我最初是通过扩展经典的生产者-消费者模型来实现的。我在收集器和变压器之间放置了一个缓冲块,在变压器和通知器之间放置了另一个。(实际上还有更多的事情发生,但这就是要点。)每个组件都独立地从其相应的缓冲区块发布和接收数据。也就是说,我觉得必须有一种更优雅的方法。

从那时起,我发现LinkTo允许块相互连接,并且除了缓冲区块之外还有专门的块,所以我开始考虑以下几点:

var extractKeywords = new TransformBlock<string, string>(ExtractKeywords);
var notifyTarget = new ActionBlock<string, string>(NotifyTarget);
extractKeywords.LinkTo(notifyTarget);

然后我可以启动不同的收集器,它们或多或少地在单独的线程上执行以下操作:

public async Task CollectAsync()
{
    while (true)
    {
        Page page = await website.DownloadLatestPageAsync();
        foreach (string comment in page.Comments())
        {
            transformer.Post(comment);
        }
        await Task.Delay(ArbitraryDelay);
    }
}

收集器将发布到前面片段中看到的转换块。然而,我确实对这种方法有所保留。首先,(这可能是由于我对异步、多线程和 TPL 不熟悉)我担心发布到转换块会产生延迟。理想情况下,我希望收集器花费尽可能少的时间将数据传输到变压器,但我似乎无法找到一种干净的方式来做到这一点。如果变压器很慢,呼叫Post似乎会阻塞很长一段时间,我也不确定是否SendAsync会处理这个问题。Task.Run似乎是个坏主意……但我不知道。感觉就像我很接近,但缺少一些简单的东西。

标签: c#.nettask-parallel-library

解决方案


推荐阅读