繁体   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