TheKeyboarder
TheKeyboarder

Reputation: 99

Async seems to be synchronous

I am not sure if I am missing something here but more for loop seems to be executing synchronously even though I await all tasks out side of it.

Here is my code below:

static void Main(string[] args) {
    var t = Start();
}

public static async Task < List < Task < TaskInfo >>> Start() {
    var listOfTasks = new List < Task < TaskInfo >> ();
    for (var i = 0; i <= 100; i++) {
        var process = new Processor();
        listOfTasks.Add(process.Process(i));
    }

    await Task.WhenAll(listOfTasks);
    return listOfTasks;
}

I pass in the taskId to log out just to see the order the tasks execute.

Am I missing something really obvious here?

EDIT:

Changed code to this based on the answers and comments below and it still appears synchronously:

    public class StartWork
{
    public int TaskId { get; set; }
    public Processor Processor { get;}

    public StartWork()
    {
        Processor = new Processor();
    }
}

    static void Main(string[] args)
    {
        var t = Start();
    }

    public static async Task<TaskInfo[]> Start()
    {
        var tasks = new List<StartWork>();

        for (int i = 1; i < 100; i++)
        {
            var work = new StartWork
            {
                TaskId = i
            };
            tasks.Add(work);
        }

        return await Task.WhenAll(tasks.Select(i => i.Processor.Process(i.TaskId)));
    }

The function I am calling in processor class:

 public Task<TaskInfo> Process(int taskId)
    {
        try
        {
            taskId = taskId + 1;
            stopwatch.Start();
            using (var bus = RabbitHutch.CreateBus(xxDev))
            {
                @event = new AutoResetEvent(false);

                var replyTo = Guid.NewGuid().ToString();
                var messageQueue = bus.Advanced.QueueDeclare(replyTo, autoDelete: true);

                bus.Advanced.Consume(messageQueue, (payload, properties, info) =>
                {
                    ReceivePdf(payload, properties, info);
                    return Task.FromResult(0);
                });

                taskInfo.InputFile = inputFile;
                var html = File.ReadAllText(inputFile);
                taskInfo.Html = html;

                var message = PrepareMessage(new RenderRequest()
                {
                    Html = Encoding.UTF8.GetBytes(html),
                    Options = new RenderRequestOptions()
                    {
                        PageSize = "A4",
                        ImageQuality = 70,
                        PageLoadRetryAttempts = 3
                    }
                });

                var correlation = Guid.NewGuid().ToString();
                Console.WriteLine($"CorrelationId: {correlation}, TaskId {taskId}");

                var props = new MessageProperties
                {
                    CorrelationId = correlation,
                    ReplyTo = replyTo,
                    Expiration = "6000"
                };

                Publish(bus, props, message);
                taskInfo.CorrelationId = Guid.Parse(correlation);
                @event.WaitOne();
                stopwatch.Stop();
                taskInfo.TimeTaken = stopwatch.Elapsed;
                return Task.FromResult(taskInfo);
            }
        }
        catch (Exception e)
        {
            taskInfo.OutputFile = Empty;
            return Task.FromResult(taskInfo);
        }
    }

        void ReceivePdf(byte[] payload, MessageProperties properties, MessageReceivedInfo info)
    {
        var file = Format(outputFile, properties.CorrelationId);
        taskInfo.OutputFile = file;

        Console.WriteLine("Output written to " + file);

        File.WriteAllBytes(file, payload);

        var remaining = Interlocked.Decrement(ref outstandingRequests);

        if (remaining == 0)
        {
            @event.Set();
        }
    }

Upvotes: 0

Views: 224

Answers (2)

Stefano Balzarotti
Stefano Balzarotti

Reputation: 1904

First async doesn't mean multithread, async is used to run background task without blocking UI or to run I/O operations without bloking main thread. Usually the operating system handles async with multithreading, but there is no guarantee. If you want be sure to start multiple threads use Thread.Start.

Anyway in your code you force your code to run synchronously, because you call the async method start in the Main method without await.

You need to change the code to:

static async void Main(string[] args)
{
   var t = await Start();
}

or without waiting to (but the program risk to terminate before the task complete):

static void Main(string[] args)
{
   Task.Run(async () => {
       var t = await Start();
   });
}

Upvotes: 0

Athanasios Kataras
Athanasios Kataras

Reputation: 26450

This is a synchronous task

listOfTasks.Add(process.Process(i));

You are just adding items to the list.

This is also a synchronous task

process.Process(i);

The task that the function above returns is asynchronous and it will execute asynchronously in the whenAll call of your code.

Bear in mind that when all will wait for all tasks to run and if the task is trivial, since they start one after the other, will most times run sequentially by chance.

You will see some difference if the task code executed differentiated in execution time based on input.

Upvotes: 1

Related Questions