3
votes

In a console application I use async-await as follows:

static void Main(string[] args)
{
    // Task<Model>
    var taskGetModel = Testcases.GetModel(22);
    taskGetModel.Wait();

    // Task without TResult
    var taskSaveModel = Testcases.SaveModel(taskGetModel.Result);
    taskSaveModel.Wait();
}

public async static Task<Model> GetModel(int number)
{
    var baseData = await serviceagent.GetBaseData();

    var model = await serviceagent.GetModel(baseData, number);

    model.BaseData = baseData;

    return model;
}

public static async Task SaveModel(Model model)
{
    await serviceagent.SaveModel(model);
}

// as for the service methods:
public async Task SaveModel(Model model)
{
    // the method `.ToCommunication` has the signature:
    // public async Task<CommunicationModel> ToCommunication
    var commodel = await new Mapper().ToCommunication(model);
    // Proxy is the interface to the servce (ChannelFactory etc.)
    // the method `.Save` has the signature:
    // public async Task Save(CommunicationModel model)
    await Proxy.Save(commodel);
}
public async Task<Model> GetModel(BaseData baseData, int number)
{
   // NOT a task: CommunicationModel GetCommunicationModel
   var commodel = Proxy.GetCommunicationModel(baseData, number);
   // signature with Task: public async Task<Model> ToModel
   return await new Mapper().ToModel(baseData, commodel);
} 

In static main, the Task<TResult> gives the nice result that the function GetModel returns immediately with the Task and I can wait for its result to finish.

Why does the Task in SaveModel not return immediately?
The await in SaveModel is already awaited in that method. Is it because it doesn not have a TResult?

1
Well what does your SaveModel method do? Is it genuinely async? It would really help if you would provide a short but complete program demonstrating the problem. Note that writing an async method whose only await is the very last thing is rarely useful - it's just wrapping one async (presumably) operation in another. - Jon Skeet
This is not a difference between Task vs Task<TResult>. It is most likely a difference between how SaveModel and GetBaseData are implemented. - Marc Gravell
You should not be synchronously wait on asynchronous methods like this. If you had a message loop you'd be deadlocking your program, and even if you're not you're completely defeating the purpose of using asynchronous methods. - Servy
An awaitable method really just returns either Task or Task<T> (or another awaitable type), it doesn't guarantee that it actually is asynchronous in its entirety (or at all, it could return a Task with an IsCompleted state of true). Without seeing what Proxy.Save does, ToCommunication does or what Mapper() is, it's impossible to tell for sure. - Peter Ritchie
@TimS. You can write a synchronous program that does work in parallel; there are tons of ways of doing that, if that's what is needed. Here, given that the underlying work is largely asynchronous IO, I'd say the whole program should be asynchronous, rather than synchronous, but doing half and have is giving you the worst of both worlds. The program should be one or the other; either is capable of working. - Servy

1 Answers

2
votes

Works for me. The following code...

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Task<Model>
            var sw = Stopwatch.StartNew();
            Console.WriteLine("1: " + sw.Elapsed);
            var taskGetModel = GetModel(22);
            Console.WriteLine("2: " + sw.Elapsed);
            taskGetModel.Wait();
            Console.WriteLine("3: " + sw.Elapsed);

            // Task without TResult
            var taskSaveModel = SaveModel(taskGetModel.Result);
            Console.WriteLine("4: " + sw.Elapsed);
            taskSaveModel.Wait();
            Console.WriteLine("5: " + sw.Elapsed);
        }

        public async static Task<Model> GetModel(int number)
        {
            var baseData = await service.GetBaseData();

            var model = await service.GetModel(baseData, number);

            model.BaseData = baseData;

            return model;
        }

        public static async Task SaveModel(Model model)
        {
            await service.SaveModel(model);
        }

        static Service service = new Service();
        class Service
        {
            public Task SaveModel(Model model)
            {
                return Task.Delay(1000);
            }

            public async Task<Model> GetModel(object baseData, int number)
            {
                await Task.Delay(1000);
                return new Model();
            }

            public async Task<object> GetBaseData()
            {
                await Task.Delay(1000);
                return new object();
            }
        }
    }
    public class Model
    {
        public object BaseData { get; set; }
    }
}

Outputs something like

1: 00:00:00.0000102
2: 00:00:00.0087409
3: 00:00:02.0321182
4: 00:00:02.0356848
5: 00:00:03.0459510

Which is exactly what you'd expect. Maybe your service.SaveModel method is not implemented in a truly asynchronous fashion, and does a long-running operation before surrendering control? Here's an example of a bad implementation:

public Task SaveModel(Model model)
{
    Thread.Sleep(1000);
    return Task.Delay(0);
}

This makes it output:

1: 00:00:00.0000303
2: 00:00:00.0090621
3: 00:00:02.0345882
4: 00:00:03.0362871
5: 00:00:03.0365073