【问题标题】:C# Correct pattern for Paralle.For async await method [closed]Parallel.For异步等待方法的C#正确模式[关闭]
【发布时间】:2016-09-24 02:06:17
【问题描述】:

我想对这种方法提出一些建议。这是一个很好的模式吗?还是会有其他更好的模式?

    public async Task<PortfolioLoans> GetSampleOfPortfolioLoanNumbers(int count = 1)
    {
        var sqlConn = new SqlConnection(this.DataMineConnectionString);
        var pfLoans = new PortfolioLoans();

        var ts = new ThreadSafeList<PortfolioLoan>();

        try
        {
          await Task.Run(() => {
              Parallel.For(0, count, async i =>
              {
               var loans = await sqlConn.QueryAsync("dbo.spGetSampleApplicationIDs", Parameters.Empty, Query.Returns<PortfolioLoan>());
               ts.AddRange(loans);
              });

              pfLoans.Loans.AddRange(ts.Clone());
          });

        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }

        return pfLoans;
    }

【问题讨论】:

  • pfLoans.Loans 是一个列表吗?如果是这样,那不是线程安全的
  • @AndrewMorton - 我正在使用Insight Databasemicro orm
  • @DanielA.White - 感谢您指出这一点。我会解决的。
  • 我唯一要质疑的是使用任务和并行。For...你在 UI 线程上吗?
  • @Mike_G - 没有用户界面

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


【解决方案1】:

Parallel 类在异步/等待之前发布。它支持使用async in 作为方法boday。当您使用async 时,它会创建一个匿名的async void 函数,因此它无法判断工作何时完成,并且在您开始的任务完成之前完成。使用 async/await 的更现代的一组类是 TPL Dataflow

其次,sqlConn 不是线程安全的,你不能同时在多个线程中使用它。你必须为每个线程创建一个新的连接对象。

最后我会把钱放在pfLoans.LoansList&lt;T&gt; 或类似的东西上,并且不是线程安全的。您不能从多个线程调用未设计为处理来自多个线程的调用的函数。您必须锁定对 AddRange 的调用,或者将类更改为线程安全的类。

【讨论】:

  • 既然你提到了它,我已经经历了你提到的Paralle.Forasync的确切场景。现在我知道它为什么要这么做了。谢谢
【解决方案2】:

稍微清理一下并添加线程安全性:

   public async Task<PortfolioLoans> GetSampleOfPortfolioLoanNumbers(int count = 1)
   {

        var pfLoans;

        try
        {
           pfLoans = await Task.Run(async () => {
              var sqlConn = new SqlConnection(this.DataMineConnectionString);
              var pls = new Portfolioloans();
              for(var i=0; i<count; i++)
              {
                  var loans = await sqlConn.QueryAsync("dbo.spGetSampleApplicationIDs", Parameters.Empty, Query.Returns<PortfolioLoan>());
                  pls.Loans.AddRange(loans);
              }
             return pls;
      });

    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
    }

    return pfLoans;
}

除非这些查询花费的时间太长,否则我不知道我是否会将它们放在 Parallel.For 中

编辑:我错过了 sqlConn.QueryAsync 上的异步,如果担心阻塞,那么你甚至不需要任务,因为 QueryAsync 已经是异步的。

  public async Task<PortfolioLoans> GetSampleOfPortfolioLoanNumbers(int count = 1)
  {
    var pfLoans = new PortfolioLoan();

    try
    {
          var sqlConn = new SqlConnection(this.DataMineConnectionString);
          var pls = new Portfolioloans();
          for(var i=0; i<count; i++)
          {
              var loans = await sqlConn.QueryAsync("dbo.spGetSampleApplicationIDs", Parameters.Empty, Query.Returns<PortfolioLoan>());
              pfLoans.Loans.AddRange(loans);
          }
   }
   catch (Exception ex)
   {
      Debug.WriteLine(ex.Message);
   }

   return pfLoans;
}

【讨论】:

  • 通过 Visual Studio 运行您的代码,您在 Task.Run(() =&gt; { 中的 () 之前忘记了异步,已为您修复。
  • @ScottChamberlain 是的,谢谢,我完全忽略了异步查询
  • 那么我应该使用await Task.Run(async () =&gt; 还是不使用?这里有点困惑。我非常感谢所有的反馈。
  • @chdev77 你不需要它。你已经在使用 async/await 你不需要额外的 Task.Run。
  • 我不会。另外,请记住,仅仅因为您使用 Parallel.For,并不能保证它会并行运行。
猜你喜欢
  • 2020-08-01
  • 1970-01-01
  • 2021-12-20
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
相关资源
最近更新 更多