【问题标题】:To call method asynchronously from loop in c#在 C# 中从循环中异步调用方法
【发布时间】:2015-05-13 06:20:40
【问题描述】:

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

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

【问题讨论】:

    标签: c# multithreading asynchronous


    【解决方案1】:

    你永远不应该像这样使用Thread.Sleep,因为你不知道3000ms是否足够,你应该使用Task类而不是使用Thread类,这是更好的选择,因为它提供了一些额外的功能,线程池管理等等应该将您的函数结果保存到数据库中。如果您能够使用 .NET 4.5,您应该尝试使用 async/await 功能。

    更简单的解决方案:

    Parallel.ForEach
    

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

    【讨论】:

    • 感谢您的建议,我会试一试
    【解决方案2】:

    首先,不惜一切代价避免在您的代码中开始新的Threads;这些线程会像一颗坍塌的星星一样吸走你的内存,因为每个线程都分配了大约 1MB 的内存。

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

    1. ThreadPool.QueueUserWorkItem 用于旧版本的 .NET Framework
    2. Parallel.ForEach 用于 .NET Framework 4
    3. asyncawait 用于 .NET Framework 4.5
    4. TPL Dataflow 也适用于 .NET Framework 4.5

    您显示的代码非常适合 Dataflow,而且我不建议在此处使用 async/await,因为它的使用会改变您的示例是一种 fire and forget 机制,这违反了使用 async/await 的建议。

    要使用Dataflow,您需要大体上:

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

    【讨论】:

      【解决方案3】:

      尝试如下异步。

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

      【讨论】:

        猜你喜欢
        • 2023-03-11
        • 1970-01-01
        • 2014-10-09
        • 2011-01-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-21
        相关资源
        最近更新 更多