簡體   English   中英

在TPL數據流循環中完成

[英]Completion in TPL Dataflow Loops

我在確定如何在循環TPL數據流中檢測完成時遇到問題。

我在數據流的一部分中有一個反饋循環,它向遠程服務器發出GET請求並處理數據響應(用更多數據流轉換這些數據流然后提交結果)。

數據源將其結果拆分為1000條記錄的頁面,並不會告訴我它有多少頁面可供我使用。 我必須繼續閱讀,直到我得到不到一整頁的數據。

通常頁數是1,經常是10,每次我們都有1000。

我有很多要求在開始時提取。
我希望能夠使用一個線程池來處理這個,所有這些都很好,我可以排隊多個數據請求並同時請求它們。 如果我偶然遇到需要獲取大量頁面的實例,我想要使用我的所有線程。 我不想留下一個線索,而其他人已經完成了。

我遇到的問題是當我將此邏輯放入數據流時,例如:

//generate initial requests for activity
var request = new TransformManyBlock<int, DataRequest>(cmp => QueueRequests(cmp));

//fetch the initial requests and feedback more requests to our input buffer if we need to
TransformBlock<DataRequest, DataResponse> fetch = null;
fetch = new TransformBlock<DataRequest, DataResponse>(async req =>
{
    var resp = await Fetch(req);

    if (resp.Results.Count == 1000)
        await fetch.SendAsync(QueueAnotherRequest(req));

    return resp;
}
, new ExecutionDataflowBlockOptions {  MaxDegreeOfParallelism = 10 });

//commit each type of request
var commit = new ActionBlock<DataResponse>(async resp => await Commit(resp));

request.LinkTo(fetch);
fetch.LinkTo(commit);

//when are we complete?

QueueRequests生成IEnumerable<DataRequest> 我立刻將下一個N頁請求排隊,接受這意味着我發送的呼叫數量比我需要的多一些。 DataRequest實例共享一個LastPage計數器,以避免無意中發出我們知道在最后一頁之后的請求。 這一切都很好。

問題:
如果我通過將更多請求反饋到fetch的輸入緩沖區來循環,就像我在本例中所示,那么我對如何發信號(甚至檢測)完成有疑問。 我無法在請求提取時設置完成,因為一旦設置完成,我就無法再反饋了。

我可以在fetch上監視輸入和輸出緩沖區是否為空,但我認為當我設置完成時,我仍然冒着獲取仍然忙於請求的風險,因此阻止了對其他頁面的排隊請求。

我可以通過某種方式知道提取繁忙(輸入或忙於處理輸入)。

我錯過了解決這個問題的明顯/直接的方法嗎?

  • 我可以在fetch中循環,而不是排隊更多的請求。 問題是我希望能夠使用設置的最大線程數來限制我正在對遠程服務器做的事情。 塊內的並行循環是否可以與塊本身共享調度程序,並通過調度程序控制生成的線程數?

  • 我可以為fetch創建一個自定義轉換塊來處理完成信號。 對於這樣一個簡單的場景,似乎有很多工作要做。

非常感謝您提供的任何幫助!

在TPL Dataflow中,您可以使用DataflowLinkOptions 鏈接塊 ,並指定塊完成傳播

request.LinkTo(fetch, new DataflowLinkOptions { PropagateCompletion = true });
fetch.LinkTo(commit, new DataflowLinkOptions { PropagateCompletion = true });

之后,您只需為request塊調用Complete()方法,就完成了!

// the completion will be propagated to all the blocks
request.Complete();

您應該使用的最后一件事是最后一個塊的Completion task屬性:

commit.Completion.ContinueWith(t =>
    {
        /* check the status of the task and correctness of the requests handling */
    });

現在我已經為fetch塊添加了一個簡單的繁忙狀態計數器: -

int fetch_busy = 0;

TransformBlock<DataRequest, DataResponse>  fetch_activity=null;
fetch = new TransformBlock<DataRequest, ActivityResponse>(async req => 
    {
        try
        {
            Interlocked.Increment(ref fetch_busy);
            var resp = await Fetch(req);

            if (resp.Results.Count == 1000)
            {
                await fetch.SendAsync( QueueAnotherRequest(req) );
            }

            Interlocked.Decrement(ref fetch_busy);
            return resp;
        }
        catch (Exception ex)
        {
            Interlocked.Decrement(ref fetch_busy);
            throw ex;
        }
    }
    , new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 10 });

然后我用它來表示完整如下: -

request.Completion.ContinueWith(async _ =>
    {
        while ( fetch.InputCount > 0 || fetch_busy > 0 )
        {
            await Task.Delay(100);
        }

        fetch.Complete();
    });

哪個看起來不是很優雅,但我認為應該工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM