【问题标题】:Guidance needed with Task and async await [duplicate]任务和异步等待需要指导[重复]
【发布时间】:2018-06-01 09:25:27
【问题描述】:

在浏览了很多文章和视频后,我仍然对异步编程有疑问。我正在做一个项目,在服务层中,我将所有方法都创建为异步。所有返回Task <T>Task(我确保不返回无效)。 现在到了这个问题。我的 Api 调用异步方法,这些方法在内部调用其他异步方法,甚至可能调用其他异步方法。所以每次遇到异步方法时我都在等待。 我认为这种方法的缺点是因为每次遇到异步时我都在等待结果,这将花费很多时间。例如:

public async Task<xyz> DoMainTask(int ActionId, int ItemId, int UserId)
{
   await DoTask1(ActionId,ItemId,UserId);  3 sec
   await DoTask2(ActionId,ItemId,UserId);  3 sec
   await DoTask3(ActionId,ItemId,UserId);  3 sec
}

所以我不想等待 9 秒,因为这里的所有任务都是相互独立的。 我想做类似的事情:

public async Task<xyz> DoMainTask(int ActionId, int ItemId, int UserId)
{
   List<Task> lst = new List<Task>();
   t1= DoTask1(ActionId,ItemId,UserId);  
   lst.Add(t1);

   t2 = DoTask2(ActionId,ItemId,UserId);
   lst.Add(t2);

   t3 = DoTask3(ActionId,ItemId,UserId);
   lst.Add(t3);

   await Task.WhenAll(lst);

   // do some work
   return xyz;
}

这可能需要大约 5-6 秒。我该怎么做呢? 每当我尝试使用第二种方法时,它都会出错:

在前一个异步操作完成之前在此上下文上启动了第二个操作

DoTask1 定义为:

  public async Task DoTask1 (int ActionId, int ItemId, int UserId)
    {
        try
        {
            DailyActivityPoint dailyActivityPoint = new DailyActivityPoint()
            {
                ActionId = ActionId,
                CreatedDate = DateTime.Now,
                ItemId = ItemId,
                UserId = UserId,
                PointsAccumulated = await GetPointsAwardedForAction(ActionId)
            };

            _entities.DailyActivityPoints.Add(dailyActivityPoint);
            await _entities.SaveChangesAsync();
        }
        catch (Exception ex)
        {

        }
    }

在 DoTask1 内部,我也在调用异步方法。

如何做到这一点,最佳做法是什么?

【问题讨论】:

  • 对 I/O 等非 cpu 绑定的东西使用 async/await,对更大/更长的计算等 cpu 绑定的东西使用线程。
  • 我现在无法启动我的 Visual Studio 来帮助你,但一定要在 Task.WhenAll 前面放一个 await
  • 我做了。忘记放在这里:p我会编辑帖子。
  • 您的问题是您不能在多个任务中重用实体框架。您需要为每个任务创建一个新的 dbcontext。见stackoverflow.com/questions/36036401/…
  • 是的。我正在使用 EF 开发一个 WebAPI2 项目。您分享的文章中给出的解决方案是解决用户使用 IOC 的问题。我不是。至少在这个模块中没有。没有为 DbContext 创建接口,仅用于我创建的服务。尽管我在属于同一项目的不同模块中使用了 autofac,但不认为它会以任何方式影响它。我是否需要将我的子方法创建为 Task insted of async Task.

标签: c# entity-framework async-await task-parallel-library


【解决方案1】:

我相信您遇到了here 所述的线程安全问题。如果是这样,您需要确保每个进入 EF 的“等待”都使用自己的 DbContext 实例。

所以,请确保您没有使用 DbContext 单例;如果可以的话,随时实例化一个新容器,或者像他在链接中那样使用容器(容器是你的朋友)

【讨论】:

  • 啊 Therad 安全...好的。谢谢您的帮助。我会尽量记住这些事情:)
  • 从您的代码 sn-p 猜测,您的 _entities 似乎直接是您的 DbContext,所以可能这就像一个单身人士。如果您在函数中创建和销毁本地 _entities,这可能会成功。
  • 是的 .. 它成功了。在方法中创建上下文而不是使用共享上下文,它工作得很好......谢谢:)
  • 很高兴为您提供帮助!
【解决方案2】:

也许您应该在编写异步方法时使用此指南

指南:

  1. 按照编写常规方法的正常方式编写方法,然后将其转换为异步方法。

  2. 在方法声明中使用 async 关键字。

    public async Task<int> ExampleMethodAsync()  
    {  
        // . . . .  
    } 
    
  3. 在调用异步进程/方法的代码中使用 await 关键字。

      int resultValue = await ExampleMethodAsync();
    

注意: await 只能用在 async 关键字修改的异步方法中。

  1. 在异步方法中使用正确的返回类型之一以获得结果

    return types for an async method need to be one of the following:
    
    • 如果您的方法有一个返回语句,其中操作数的类型为 TResult,则为任务。
    • 如果您的方法没有 return 语句或有一个没有操作数的 return 语句,则执行任务。
    • 如果您正在编写异步事件处理程序,则无效。
  2. 在调用方法名称的末尾添加“Async”后缀。这不是必需的,但可以考虑 在 C# 中编写异步方法的约定。

    public async Task<int> ExampleCallingMethodAsync()  
    {  
        // . . . .  
    }
    
  3. 在异步方法代码中至少包含一个等待表达式。 在等待表达式处暂停异步方法不会 构成方法的退出,finally 块不运行。

    public async Task<int> ExampleMethodAsync()  
    {  
     //some code
     string pageContents = await client.GetStringAsync(uri);   
     //some more code
     return pageContents.Length;
    }
    

    注意:

    1. 您必须调整异步方法返回的内容。返回的对象必须与你的异步方法的类型 Task&lt;T&gt; 匹配
    2. 如果异步方法不使用等待运算符来标记暂停点,则该方法 尽管有 async 修饰符,但仍像同步方法一样执行。编译器发出一个 此类方法的警告。

下面是转换为异步方法的典型方法

示例

    private void WebPage(string someURI) 
 { 
    WebClient webClient = new WebClient(); 
    string pageContent = webClient.DownloadString(someURI);
    Console.WriteLine(pageContent);
 }

改为:

   private async void WebPageAsync(string someURI)
 { 
  WebClient webClient = new WebClient();
  string pageContent = await webClient.DownloadStringTaskAsync(someURI); 
  Console.WriteLine(pageContent); 
 }

我希望这有帮助吗?

【讨论】:

  • 感谢您的帖子,虽然我已经知道了这么多。 :)
猜你喜欢
  • 2018-02-23
  • 2013-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 2014-03-18
  • 1970-01-01
  • 2014-09-06
相关资源
最近更新 更多