【问题标题】:Different behaviour of Task vs. Task<TResult>Task 与 Task<TResult> 的不同行为
【发布时间】:2014-09-23 14:19:58
【问题描述】:

在控制台应用程序中,我按如下方式使用 async-await:

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

在静态 main 中,Task&lt;TResult&gt; 给出了很好的结果,函数 GetModel 立即返回 Task,我可以等待它的结果完成。

为什么SaveModel中的Task没有立即返回?
SaveModel 中的 await 已经在该方法中等待。 是因为没有TResult吗?

【问题讨论】:

  • 那么您的SaveModel 方法是做什么的?它真的是异步的吗?如果您能提供一个简短但完整的程序来演示该问题,那将非常有帮助。请注意,编写一个只有await 是最后一件事的异步方法很少有用 - 它只是将一个异步(可能)操作包装在另一个中。
  • TaskTask&lt;TResult&gt; 之间没有区别。 SaveModelGetBaseData 的实现方式很可能有所不同。
  • 你不应该同步等待这样的异步方法。如果你有一个消息循环,你就会死锁你的程序,即使你没有,你也完全违背了使用异步方法的目的。
  • 一个真正的等待方法只是返回TaskTask&lt;T&gt;(或其他等待类型),它不能保证它实际上是是 i> 完全异步(或者完全异步,它可以返回TaskIsCompleted 状态true)。如果没有看到 Proxy.Save 做什么、ToCommunication 做什么或 Mapper() 是什么,就无法确定。
  • @TimS。您可以编写一个并行工作的同步程序;如果需要的话,有很多方法可以做到这一点。在这里,考虑到底层工作主要是异步 IO,我想说整个程序应该是异步的,而不是同步的,但是做一半和有就是给你两全其美。程序应该是其中之一;两者都可以工作。

标签: c# async-await


【解决方案1】:

为我工作。以下代码...

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

输出类似

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

这正是您所期望的。也许您的 service.SaveModel 方法没有以真正的异步方式实现,并且在放弃控制之前执行了长时间运行的操作?这是一个糟糕的实现示例:

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

这使它输出:

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

【讨论】:

  • 你给出的例子工作得很好,这也是我想象的应该如何工作。所以我必须进一步调查,以发现我看到的行为差异来自哪里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 2020-11-23
  • 1970-01-01
相关资源
最近更新 更多