Tweety01
Tweety01

Reputation: 176

To call method asynchronously from loop in c#

I have a requirement where I have pull data from Sailthru API.The problem is it will take forever if I make a call synchronously as the response time depends on data.I have very much new to threading and tried out something but it didn't seems to work as expected. Could anyone please guide. Below is my sample code

    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);
        }

    }

I want the Function call 1 to start asynchronously(or may be after gap of 3 seconds to avoid load on processor). In Function call 3 I am calling the same function again if the status is not completed with delay of 3 seconds to give time for receiving response.

Also correct me if my question sounds stupid.

Upvotes: 0

Views: 323

Answers (3)

RePierre
RePierre

Reputation: 9576

First of all, avoid at all cost starting new Threads in your code; those threads will suck your memory like a collapsing star because each of those gets ~1MB of memory allocated to it.

Now for the code - depending on the framework you can choose from the following:

  1. ThreadPool.QueueUserWorkItem for older versions of .NET Framework
  2. Parallel.ForEach for .NET Framework 4
  3. async and await for .NET Framework 4.5
  4. TPL Dataflow also for .NET Framework 4.5

The code you show fits quite well with Dataflow and also I wouldn't suggest using async/await here because its usage would transform your example is a sort of fire and forget mechanism which is against the recommendations of using async/await.

To use Dataflow you'll need in broad lines:

  • one TransformBlock which will take a string as an input and will return the response from the API
  • one BroadcastBlock that will broadcast the response to
  • two ActionBlocks; one to store data in the database and the other one to call TraceService

The code should look like this:

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();

Upvotes: 1

Amol Kulkarni
Amol Kulkarni

Reputation: 50

try with async as below.

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

Upvotes: -1

MistyK
MistyK

Reputation: 6232

You should never use Thread.Sleep like that because among others you don't know if 3000ms will be enough and instead of using Thread class you should use Task class which is much better option because it provides some additional features, thread pool managing etc. I don't have access to IDE but you should try to use Task.Factory.StartNew to invoke your request asynchronously then your function GetJobwiseDetail should return some value that you want to save to database and then use .ContinueWith with delegate that should save your function result into database. If you are able to use .NET 4.5 you should try feature async/await.

Easier solution:

Parallel.ForEach

Get some details on the internet about it, you have to know nothing about threading. It will invoke every loop iteration on another thread.

Upvotes: 1

Related Questions