在c#中从循环异步调用方法

时间:2015-05-13 06:20:41

标签: c# multithreading asynchronous

我有一个要求,我从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秒,我将再次调用相同的功能,以便为接收响应提供时间。

如果我的问题听起来很愚蠢,请纠正我。

3 个答案:

答案 0 :(得分:1)

你永远不应该使用Thread.Sleep,因为在其他人中你不知道3000ms是否足够而不是使用Thread类你应该使用Task类,这是更好的选择,因为它提供了一些额外的功能,线程池管理等我无法访问IDE,但您应该尝试使用Task.Factory.StartNew异步调用您的请求,然后您的函数GetJobwiseDetail应该返回一些您要保存到数据库的值,然后使用.ContinueWith与委托,应该将您的功能结果保存到数据库中。如果您能够使用.NET 4.5,则应尝试使用async / await功能。

更简单的解决方案:

Parallel.ForEach

在互联网上获取有关它的一些细节,你必须对线程一无所知。它将在另一个线程上调用每个循环迭代。

答案 1 :(得分:1)

首先,避免不惜一切代价在代码中启动新的Thread;这些线程会像崩溃的星星那样吸引你的记忆,因为每个线程都会分配大约1MB的内存。

现在代码 - 根据框架,您可以从以下选项中选择:

  1. ThreadPool.QueueUserWorkItem适用于旧版本的.NET Framework
  2. Parallel.ForEach for .NET Framework 4
  3. asyncawait for .NET Framework 4.5
  4. TPL Dataflow也适用于.NET Framework 4.5
  5. 您展示的代码非常适合Dataflow,我也不建议在这里使用async/await因为它的用法会改变您的示例是一种fire and forget机制,这违反了使用的建议async/await

    要使用Dataflow,您需要排成一行:

    • 一个TransformBlock,它将字符串作为输入,并将从API返回响应
    • 一个BroadcastBlock将广播响应
    • 两个ActionBlock s;一个用于存储数据库中的数据,另一个用于调用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();
    

答案 2 :(得分:-1)

尝试使用async,如下所示。

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