簡體   English   中英

從C#中的循環異步調用方法

[英]To call method asynchronously from loop in c#

我有一個從Sailthru API中提取數據的要求,問題是如果我同步進行調用將花費很多時間,因為響應時間取決於數據。我對線程技術有很多新知識並嘗試了一些東西,但沒有似乎按預期工作。 任何人都可以指導。 下面是我的示例代碼

    public void GetJobId()
    { 
        Hashtable BlastIDs = getBlastIDs();

        foreach (DictionaryEntry entry in BlastIDs)
        {
            Hashtable blastStats = new Hashtable();
            blastStats.Add("stat", "blast");
            blastStats.Add("blast_id", entry.Value.ToString());

                //Function call 1
                //Thread newThread = new Thread(() =>
                //{
                    GetBlastDetails(entry.Value.ToString());
                //});
                //newThread.Start();

        }

    }

    public void GetBlastDetails(string blast_id)
    {
        Hashtable tbData = new Hashtable();
        tbData.Add("job", "blast_query");
        tbData.Add("blast_id", blast_id);

        response = client.ApiPost("job", tbData);
        object data = response.RawResponse;

        JObject jtry = new JObject();
        jtry = JObject.Parse(response.RawResponse.ToString());


        if (jtry.SelectToken("job_id") != null)
        {
             //Function call 2
            Thread newThread = new Thread(() =>
            {
                GetJobwiseDetail(jtry.SelectToken("job_id").ToString(), client,blast_id);
            });
            newThread.Start();
        }

    }

    public void GetJobwiseDetail(string job_id, SailthruClient client,string blast_id)
    {
        Hashtable tbData = new Hashtable();
        tbData.Add("job_id", job_id);

        SailthruResponse response;
        response = client.ApiGet("job", tbData);

        JObject jtry = new JObject();
        jtry = JObject.Parse(response.RawResponse.ToString());

        string status = jtry.SelectToken("status").ToString();
        if (status != "completed")
        {
            //Function call 3
            Thread.Sleep(3000);
            Thread newThread = new Thread(() =>
            {
                GetJobwiseDetail(job_id, client,blast_id);
            });
            newThread.Start();
            string str = "test sleeping thread";

        }
        else {

            string export_url = jtry.SelectToken("export_url").ToString();
            TraceService(export_url);
            SaveCSVDataToDB(export_url,blast_id);
        }

    }

我希望函數調用1異步啟動(或可能在3秒鍾的間隔后才能避免在處理器上加載)。 功能調用3中 ,如果狀態未完成(延遲3秒)以給出接收響應的時間,我將再次調用同一功能。

如果我的問題聽起來很愚蠢,也請糾正我。

您永遠不應該那樣使用Thread.Sleep ,因為除其他外,您不知道3000ms是否足夠,您應該使用Task類,而不是使用Thread類,因為它提供了一些附加功能,線程池管理等,因此它是更好的選擇。我沒有訪問IDE的權限,但是您應該嘗試使用Task.Factory.StartNew異步調用您的請求,然后您的函數GetJobwiseDetail應該返回一些要保存到數據庫的值,然后將.ContinueWith與委托一起使用,這樣可以保存您的函數結果存入數據庫。 如果您能夠使用.NET 4.5,則應嘗試使用異步/等待功能。

更簡單的解決方案:

Parallel.ForEach

在Internet上獲取有關它的一些詳細信息,而您不必了解線程。 它將在另一個線程上調用每個循環迭代。

首先,不惜一切代價避免在代碼中啟動新的Thread 這些線程將像崩潰的星星一樣消耗您的內存,因為每個線程都分配有〜1MB的內存。

現在查看代碼-根據框架,您可以從以下選項中進行選擇:

  1. ThreadPool.QueueUserWorkItem Framework的較舊版本的ThreadPool.QueueUserWorkItem
  2. .NET Framework 4的Parallel.ForEach
  3. asyncawait .NET Framework 4.5
  4. TPL Dataflow也適用於.NET Framework 4.5

您顯示的代碼非常適合Dataflow,並且我也不建議async/await這里使用async/await ,因為它的用法將轉換您的示例,是一種即發即fire and forget機制,與使用async/await的建議async/await

要使用Dataflow您需要大致Dataflow幾行:

  • 一個TransformBlock ,它將字符串作為輸入,並將返回API的響應
  • 一個BroadcastBlock ,它將廣播響應到
  • 兩個ActionBlock 一個用於將數據存儲在數據庫中,另一個用於調用TraceService

該代碼應如下所示:

var downloader = new TransformBlock<string, SailthruResponse>(jobId =>
{
    var data = new HashTable();
    data.Add("job_id", jobId);
    return client.ApiGet("job", data);
});

var broadcaster = new BroadcastBlock<SailthruResponse>(response => response);

var databaseWriter = new ActionBlock<SailthruResponse>(response =>
{
    // save to database...
})

var tracer = new ActionBlock<SailthruResponse>(response => 
{
    //TraceService() call
});

var options = new DataflowLinkOptions{ PropagateCompletion = true };

// link blocks
downloader.LinkTo(broadcaster, options);
broadcaster.LinkTo(databaseWriter, options);
broadcaster.LinkTo(tracer, options);

// process values
foreach(var value in getBlastIds())
{
    downloader.Post(value);
}
downloader.Complete();

嘗試使用異步,如下所示。

async Task Task_MethodAsync()
{
    // . . .
    // The method has no return statement.  
}

暫無
暫無

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

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