【问题标题】:Is a DbContext per thread in Parallel.ForEach safe?Parallel.ForEach 中每个线程的 DbContext 是否安全?
【发布时间】:2019-09-04 22:49:25
【问题描述】:

我现在正在签订一份合同,为使用 EF 6 作为其 ORM 的现代 SaaS SPA Web 应用程序提高后端服务的性能。我建议的第一件事是在他们当前运行单线程的后端服务中引入一些多线程。首席软件工程师表示我们不能这样做,因为 EF 6 不是线程安全的。

我不是实体框架方面的专家。我选择的 ORM 是 DevExpress 的 XPO,我已经做了类似于下面建议的事情,使用该 ORM 没有问题。这种模式在使用 EF 6 时本质上是不安全的吗?

int[] ids;
using(var db = new ApplicationDbContext())
{
    // query to surface id's of records representing work to be done
    ids = GetIdsOfRecordsRepresentingSomeTask(db);
}

Parallel.ForEach(ids, id => { 
    using(var db = new ApplicationDbContext())
    {
        var processor = new SomeTaskProcessor(db, id);
        processor.ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords();
        db.SaveChanges();
    }
});

我对此进行了研究,我同意 DbContext 不是线程安全的。我建议的模式确实使用了多个线程,但是单个 DbContext 只能由单个线程以单线程方式访问。领导告诉我,DbContext 本质上是一个隐藏的单例,这段代码最终会弄乱数据库。我找不到任何东西来支持这种说法。这方面的领导正确吗?

谢谢

【问题讨论】:

  • LGTM。我们不应该对DbContext 使用多线程的主要原因是因为ADO.NET 不允许并发活动的DbCommand 执行(即使在使用async/await 的单线程环境中也是如此)。也就是说,请确保不要运行太多线程,因为这里的瓶颈将是数据库服务器。

标签: c# multithreading entity-framework parallel-processing entity-framework-6


【解决方案1】:

您的模式是线程安全的。但是,至少对于 SQL Server,如果您的并发性太高,您会发现总吞吐量随着数据库资源争用的增加而下降。

理论上,Parallel.ForEach 优化了线程数,但在实践中,我发现它在我的应用程序中允许过多的并发。

您可以使用ParallelOptions 可选参数来控制并发。测试您的用例,看看默认并发是否适合您。

您的评论:请记住,无论如何,现在我们在上面的代码中讨论的是 100 个 id,其中大多数 id 代表的工作最终不会对数据库进行任何更改并且是短暂的满手可能需要几分钟才能完成并最终将 10 条新记录添加到数据库中。您会立即推荐什么 MaxDegreesOfParallelism 值?

根据您的一般描述,可能是 2-3,但这取决于数据库密集型 ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords 的程度(相对于执行 CPU 密集型活动或等待来自文件的 IO、Web 服务调用等)。除此之外,如果该方法主要执行数据库任务,您可能会遇到锁定争用或压倒您的 IO 子系统(磁盘)。在您的环境中进行测试以确定。

可能值得探讨为什么ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords 需要这么长时间才能完成给定的 ID。

更新

这里有一些测试代码来证明线程不会相互阻塞并且确实是同时运行的。为了简单起见,我删除了 DbContext 部分,因为它不会影响线程问题。

class SomeTaskProcessor
{
    static Random rng = new Random();
    public int Id { get; private set; }
    public SomeTaskProcessor(int id) { Id = id; }
    public void ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords()
    {
        Console.WriteLine($"Starting ID {Id}");
        System.Threading.Thread.Sleep(rng.Next(1000));
        Console.WriteLine($"Completing ID {Id}");
    }
}
class Program
{
    static void Main(string[] args)
    {
        int[] ids = Enumerable.Range(1, 100).ToArray();

        Parallel.ForEach(ids, id => {
                var processor = new SomeTaskProcessor(id);
                processor.ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords();
        });
    }
}

【讨论】:

  • 感谢埃里克的回答。请记住,无论如何,现在我们在上面的代码中讨论了 100 个 id,其中大多数 id 代表的工作最终不会对数据库进行任何更改并且是短暂的,而满手可能需要几分钟才能完成并最终将 10 条新记录添加到数据库中。您会立即推荐什么 MaxDegreesOfParallelism 值?
  • 对于上面如此更新的评论,我的回答有点长。
  • 啊——是的,我应该包括那个细节。在这种情况下,正在执行的任务非常受 IO 限制,需要调用第三方 Web API 来收集处理所需的信息。一旦收集了所有数据,就会对其进行处理,并可能导致需要将一些新记录添加到数据库中。在处理阶段有一些查询可以收集一些额外的信息,但所有这些都运行良好。
  • 在这种情况下,您可能会获得相当高的并发性。开始时不要在 Parallel.ForEach 中指定任何并发限制,看看它是如何执行的。
  • 恐怕在这种情况下并行性不会有帮助。
【解决方案2】:

问题 #1 - 内存分配

每次迭代都会创建DbContext 的实例,这将导致内存分配。垃圾收集器将需要处理这些分配。如果存在持续的内存压力,最终会导致应用程序级别的性能下降。

问题 #2 - SQL 过载

根据上面写的,在每次迭代期间,您将调用SaveChanges(),这可能会调用数据库。如果对数据库的调用是/将是资源密集型的,您最终可能会得到性能不佳的数据库。

问题 #3 - 线程阻塞

SaveChanges() 是同步的,会阻塞线程。在调用数据库需要大量时间的情况下,线程只会坐下来等待。与 3rd 方 API 调用相同。不会有或只有很小的性能提升。

问题 #4 - Parallel.For* 方法不能保证并行运行

请务必记住,Parallel.For、Parallel.ForEach 或 ForAll 循环中的各个迭代可以但不必并行执行。

我认为这是不言自明的。

Potential Pitfalls with PLINQ

Understanding Speedup in PLINQ


我正在阅读问题和 Eric J. 下的 cmets。恐怕解决问题的方法存在问题,并行性无济于事。

感谢埃里克的回答。请记住,无论如何,现在我们在上面的代码中讨论了 100 个 id,其中大多数 id 代表的工作最终不会对数据库进行任何更改并且是短暂的,而满手可能需要几分钟才能完成并最终将 10 条新记录添加到数据库中。您会立即推荐什么 MaxDegreesOfParallelism 值?

啊-是的,我应该包括那个细节。在这种情况下,正在执行的任务非常受 IO 限制,需要调用第三方 Web API 来收集处理所需的信息。一旦收集了所有数据,就会对其进行处理,并可能导致需要将一些新记录添加到数据库中。在处理阶段有一些查询可以收集一些额外的信息,但所有这些都运行良好。

如果我没有完全错,问题是同步调用第 3 方 API,这会阻塞线程。

我认为异步方法可能有助于从 3rd 方 API 收集数据。

如果没有必要,也有助于不分配堆内存。您只需一个 DbContext 实例和一个数据库调用即可在执行结束时保存更改。


更新

Eric J. 添加了测试以测试线程不会相互阻塞(它们不会)并测试并发执行。下面的代码是他在回答中提供的原始代码。

class SomeTaskProcessor
{
    static Random rng = new Random();
    public int Id { get; private set; }
    public SomeTaskProcessor(int id) { Id = id; }
    public void ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords()
    {
        Console.WriteLine($"Starting ID {Id}");
        System.Threading.Thread.Sleep(rng.Next(1000));
        Console.WriteLine($"Completing ID {Id}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        int[] ids = Enumerable.Range(1, 100).ToArray();

        Parallel.ForEach(ids, id => {
                var processor = new SomeTaskProcessor(id);
                processor.ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords();
        });
    }
}

我做了类似的测试。我创建了服务器来模拟延迟响应为 1 秒的 Internet IO。在每次测试运行 API 服务器启动并发出第一个请求之前。测试在 Release 配置中运行。

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase {
    // GET api/values
    [HttpGet]
    public async Task<ActionResult<IEnumerable<string>>> Get() {
        await Task.Delay(1000);

        return new string[] { "value1", "value2" };
    }
}

我已经更新了他的代码来调用 API 端点。在我的虚拟机上,它运行了 5 个线程,平均执行时间为 23 秒。

class SomeTaskProcessor {
    private HttpClient http = new HttpClient() {
        BaseAddress = new Uri("http://localhost:49890/api/values")
    };

    public int Id { get; private set; }
    public SomeTaskProcessor(int id) { Id = id; }
    public void ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords() {
        Console.WriteLine($"Starting ID {Id}. Thread Id: {Thread.CurrentThread.ManagedThreadId}");
        var response = http.GetAsync(String.Empty).GetAwaiter().GetResult();
        Console.WriteLine($"Completing ID {Id}. Response status code is {response.StatusCode}. Thread Id: {Thread.CurrentThread.ManagedThreadId}");
    }
}

class Program {
    static void Main(string[] args) {
        int[] ids = Enumerable.Range(1, 100).ToArray();;

        var stopwatch = new Stopwatch();

        stopwatch.Start();

        Parallel.ForEach(ids, id => {
            var processor = new SomeTaskProcessor(id);
            processor.ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords();
        });

        // ~23 seconds

        stopwatch.Stop();

        Console.WriteLine(stopwatch.Elapsed.ToString());
    }
}

控制台输出:

Starting ID 51. Thread Id: 4
Starting ID 1. Thread Id: 1
Starting ID 2. Thread Id: 5
Starting ID 52. Thread Id: 9
Starting ID 3. Thread Id: 12
Completing ID 51. Response status code is OK. Thread Id: 4
Starting ID 53. Thread Id: 4
Completing ID 2. Response status code is OK. Thread Id: 5
Starting ID 4. Thread Id: 5
Starting ID 55. Thread Id: 14
Starting ID 6. Thread Id: 15
Completing ID 52. Response status code is OK. Thread Id: 9
Starting ID 56. Thread Id: 9
Completing ID 1. Response status code is OK. Thread Id: 1
Starting ID 7. Thread Id: 1
Completing ID 3. Response status code is OK. Thread Id: 12
Starting ID 9. Thread Id: 12
Starting ID 58. Thread Id: 16
Completing ID 53. Response status code is OK. Thread Id: 4
Starting ID 54. Thread Id: 4
Completing ID 4. Response status code is OK. Thread Id: 5
Starting ID 5. Thread Id: 5
Starting ID 11. Thread Id: 18
Completing ID 55. Response status code is OK. Thread Id: 14
Starting ID 59. Thread Id: 14
Starting ID 61. Thread Id: 20
Completing ID 56. Response status code is OK. Thread Id: 9
Starting ID 57. Thread Id: 9
Completing ID 7. Response status code is OK. Thread Id: 1
Starting ID 8. Thread Id: 1
Completing ID 9. Response status code is OK. Thread Id: 12
Starting ID 10. Thread Id: 12
Completing ID 6. Response status code is OK. Thread Id: 15
Starting ID 12. Thread Id: 15
Starting ID 14. Thread Id: 22
Completing ID 5. Response status code is OK. Thread Id: 5
Starting ID 15. Thread Id: 5
Completing ID 54. Response status code is OK. Thread Id: 4
Starting ID 62. Thread Id: 4
Completing ID 58. Response status code is OK. Thread Id: 16
Starting ID 66. Thread Id: 16
Starting ID 68. Thread Id: 23
Completing ID 11. Response status code is OK. Thread Id: 18
Starting ID 19. Thread Id: 18
Completing ID 59. Response status code is OK. Thread Id: 14
Starting ID 60. Thread Id: 14
Starting ID 21. Thread Id: 24
Completing ID 57. Response status code is OK. Thread Id: 9
Starting ID 69. Thread Id: 9
Completing ID 12. Response status code is OK. Thread Id: 15
Starting ID 13. Thread Id: 15
Completing ID 61. Response status code is OK. Thread Id: 20
Starting ID 73. Thread Id: 20
Completing ID 10. Response status code is OK. Thread Id: 12
Completing ID 8. Response status code is OK. Thread Id: 1
Starting ID 22. Thread Id: 12
Starting ID 26. Thread Id: 1
Starting ID 75. Thread Id: 25
Completing ID 15. Response status code is OK. Thread Id: 5
Starting ID 16. Thread Id: 5
Completing ID 62. Response status code is OK. Thread Id: 4
Starting ID 63. Thread Id: 4
Completing ID 66. Response status code is OK. Thread Id: 16
Starting ID 67. Thread Id: 16
Completing ID 14. Response status code is OK. Thread Id: 22
Starting ID 30. Thread Id: 22
Starting ID 32. Thread Id: 26
Starting ID 76. Thread Id: 27
Completing ID 60. Response status code is OK. Thread Id: 14
Starting ID 77. Thread Id: 14
Completing ID 19. Response status code is OK. Thread Id: 18
Starting ID 20. Thread Id: 18
Completing ID 69. Response status code is OK. Thread Id: 9
Starting ID 70. Thread Id: 9
Completing ID 21. Response status code is OK. Thread Id: 24
Starting ID 33. Thread Id: 24
Completing ID 13. Response status code is OK. Thread Id: 15
Starting ID 35. Thread Id: 15
Completing ID 73. Response status code is OK. Thread Id: 20
Starting ID 74. Thread Id: 20
Completing ID 22. Response status code is OK. Thread Id: 12
Starting ID 23. Thread Id: 12
Completing ID 26. Response status code is OK. Thread Id: 1
Starting ID 27. Thread Id: 1
Completing ID 68. Response status code is OK. Thread Id: 23
Starting ID 81. Thread Id: 23
Starting ID 39. Thread Id: 29
Completing ID 67. Response status code is OK. Thread Id: 16
Starting ID 83. Thread Id: 16
Completing ID 75. Response status code is OK. Thread Id: 25
Completing ID 30. Response status code is OK. Thread Id: 22
Starting ID 31. Thread Id: 22
Completing ID 16. Response status code is OK. Thread Id: 5
Starting ID 87. Thread Id: 25
Starting ID 17. Thread Id: 5
Completing ID 63. Response status code is OK. Thread Id: 4
Starting ID 64. Thread Id: 4
Starting ID 89. Thread Id: 30
Completing ID 76. Response status code is OK. Thread Id: 27
Starting ID 90. Thread Id: 27
Completing ID 32. Response status code is OK. Thread Id: 26
Starting ID 40. Thread Id: 26
Completing ID 77. Response status code is OK. Thread Id: 14
Starting ID 78. Thread Id: 14
Starting ID 42. Thread Id: 31
Completing ID 70. Response status code is OK. Thread Id: 9
Starting ID 71. Thread Id: 9
Completing ID 27. Response status code is OK. Thread Id: 1
Starting ID 28. Thread Id: 1
Completing ID 74. Response status code is OK. Thread Id: 20
Starting ID 92. Thread Id: 20
Completing ID 35. Response status code is OK. Thread Id: 15
Starting ID 36. Thread Id: 15
Completing ID 81. Response status code is OK. Thread Id: 23
Starting ID 82. Thread Id: 23
Completing ID 33. Response status code is OK. Thread Id: 24
Starting ID 34. Thread Id: 24
Completing ID 20. Response status code is OK. Thread Id: 18
Starting ID 43. Thread Id: 18
Completing ID 23. Response status code is OK. Thread Id: 12
Starting ID 24. Thread Id: 12
Starting ID 96. Thread Id: 32
Completing ID 39. Response status code is OK. Thread Id: 29
Completing ID 17. Response status code is OK. Thread Id: 5
Starting ID 18. Thread Id: 5
Starting ID 47. Thread Id: 29
Completing ID 64. Response status code is OK. Thread Id: 4
Starting ID 65. Thread Id: 4
Completing ID 87. Response status code is OK. Thread Id: 25
Starting ID 88. Thread Id: 25
Completing ID 31. Response status code is OK. Thread Id: 22
Completing ID 83. Response status code is OK. Thread Id: 16
Starting ID 84. Thread Id: 16
Starting ID 49. Thread Id: 22
Starting ID 97. Thread Id: 33
Completing ID 90. Response status code is OK. Thread Id: 27
Starting ID 91. Thread Id: 27
Completing ID 40. Response status code is OK. Thread Id: 26
Starting ID 41. Thread Id: 26
Completing ID 89. Response status code is OK. Thread Id: 30
Starting ID 98. Thread Id: 30
Completing ID 78. Response status code is OK. Thread Id: 14
Starting ID 79. Thread Id: 14
Starting ID 100. Thread Id: 34
Completing ID 36. Response status code is OK. Thread Id: 15
Starting ID 37. Thread Id: 15
Completing ID 92. Response status code is OK. Thread Id: 20
Starting ID 93. Thread Id: 20
Completing ID 42. Response status code is OK. Thread Id: 31
Completing ID 28. Response status code is OK. Thread Id: 1
Starting ID 29. Thread Id: 1
Completing ID 24. Response status code is OK. Thread Id: 12
Starting ID 25. Thread Id: 12
Completing ID 82. Response status code is OK. Thread Id: 23
Completing ID 71. Response status code is OK. Thread Id: 9
Starting ID 72. Thread Id: 9
Completing ID 43. Response status code is OK. Thread Id: 18
Starting ID 44. Thread Id: 18
Completing ID 96. Response status code is OK. Thread Id: 32
Completing ID 65. Response status code is OK. Thread Id: 4
Completing ID 88. Response status code is OK. Thread Id: 25
Completing ID 49. Response status code is OK. Thread Id: 22
Starting ID 50. Thread Id: 22
Completing ID 47. Response status code is OK. Thread Id: 29
Starting ID 48. Thread Id: 29
Completing ID 18. Response status code is OK. Thread Id: 5
Completing ID 34. Response status code is OK. Thread Id: 24
Completing ID 84. Response status code is OK. Thread Id: 16
Starting ID 85. Thread Id: 16
Completing ID 97. Response status code is OK. Thread Id: 33
Completing ID 41. Response status code is OK. Thread Id: 26
Completing ID 79. Response status code is OK. Thread Id: 14
Starting ID 80. Thread Id: 14
Completing ID 100. Response status code is OK. Thread Id: 34
Completing ID 93. Response status code is OK. Thread Id: 20
Starting ID 94. Thread Id: 20
Completing ID 29. Response status code is OK. Thread Id: 1
Completing ID 98. Response status code is OK. Thread Id: 30
Starting ID 99. Thread Id: 30
Completing ID 44. Response status code is OK. Thread Id: 18
Starting ID 45. Thread Id: 18
Completing ID 25. Response status code is OK. Thread Id: 12
Completing ID 37. Response status code is OK. Thread Id: 15
Starting ID 38. Thread Id: 15
Completing ID 72. Response status code is OK. Thread Id: 9
Completing ID 91. Response status code is OK. Thread Id: 27
Completing ID 50. Response status code is OK. Thread Id: 22
Completing ID 48. Response status code is OK. Thread Id: 29
Completing ID 85. Response status code is OK. Thread Id: 16
Starting ID 86. Thread Id: 16
Completing ID 80. Response status code is OK. Thread Id: 14
Completing ID 94. Response status code is OK. Thread Id: 20
Starting ID 95. Thread Id: 20
Completing ID 99. Response status code is OK. Thread Id: 30
Completing ID 45. Response status code is OK. Thread Id: 18
Starting ID 46. Thread Id: 18
Completing ID 38. Response status code is OK. Thread Id: 15
Completing ID 86. Response status code is OK. Thread Id: 16
Completing ID 95. Response status code is OK. Thread Id: 20
Completing ID 46. Response status code is OK. Thread Id: 18
00:00:23.6046580

C:\Program Files\dotnet\dotnet.exe (process 7208) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .

然后我创建了相同代码的异步版本。执行平均需要 7 秒。

class SomeTaskProcessor {
    private HttpClient http = new HttpClient() {
        BaseAddress = new Uri("http://localhost:49890/api/values")
    };

    public int Id { get; private set; }
    public SomeTaskProcessor(int id) { Id = id; }
    public async Task ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords() {
        Console.WriteLine($"Starting ID {Id}");
        var response = await http.GetAsync(String.Empty);
        Console.WriteLine($"Completing ID {Id}. Response status code is {response.StatusCode}");
    }
}

class Program {
    static async Task Main(string[] args) {
        int[] ids = Enumerable.Range(1, 100).ToArray();;

        var stopwatch = new Stopwatch();

        stopwatch.Start();

        var tasks = ids.Select(id => {
            var processor = new SomeTaskProcessor(id);
            return processor.ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords();
        }).ToArray();

        await Task.WhenAll(tasks);

        // ~8 seconds

        stopwatch.Stop();

        Console.WriteLine(stopwatch.Elapsed.ToString());
    }
}

控制台输出:

Starting ID 1. Thread Id: 1
Starting ID 2. Thread Id: 1
Starting ID 3. Thread Id: 1
Starting ID 4. Thread Id: 1
Starting ID 5. Thread Id: 1
Starting ID 6. Thread Id: 1
Starting ID 7. Thread Id: 1
Starting ID 8. Thread Id: 1
Starting ID 9. Thread Id: 1
Starting ID 10. Thread Id: 1
Starting ID 11. Thread Id: 1
Starting ID 12. Thread Id: 1
Starting ID 13. Thread Id: 1
Starting ID 14. Thread Id: 1
Starting ID 15. Thread Id: 1
Starting ID 16. Thread Id: 1
Starting ID 17. Thread Id: 1
Starting ID 18. Thread Id: 1
Starting ID 19. Thread Id: 1
Starting ID 20. Thread Id: 1
Starting ID 21. Thread Id: 1
Starting ID 22. Thread Id: 1
Starting ID 23. Thread Id: 1
Starting ID 24. Thread Id: 1
Starting ID 25. Thread Id: 1
Starting ID 26. Thread Id: 1
Starting ID 27. Thread Id: 1
Starting ID 28. Thread Id: 1
Starting ID 29. Thread Id: 1
Starting ID 30. Thread Id: 1
Starting ID 31. Thread Id: 1
Starting ID 32. Thread Id: 1
Starting ID 33. Thread Id: 1
Starting ID 34. Thread Id: 1
Starting ID 35. Thread Id: 1
Starting ID 36. Thread Id: 1
Starting ID 37. Thread Id: 1
Starting ID 38. Thread Id: 1
Starting ID 39. Thread Id: 1
Starting ID 40. Thread Id: 1
Starting ID 41. Thread Id: 1
Starting ID 42. Thread Id: 1
Starting ID 43. Thread Id: 1
Starting ID 44. Thread Id: 1
Starting ID 45. Thread Id: 1
Starting ID 46. Thread Id: 1
Starting ID 47. Thread Id: 1
Starting ID 48. Thread Id: 1
Starting ID 49. Thread Id: 1
Starting ID 50. Thread Id: 1
Starting ID 51. Thread Id: 1
Starting ID 52. Thread Id: 1
Starting ID 53. Thread Id: 1
Starting ID 54. Thread Id: 1
Starting ID 55. Thread Id: 1
Starting ID 56. Thread Id: 1
Starting ID 57. Thread Id: 1
Starting ID 58. Thread Id: 1
Starting ID 59. Thread Id: 1
Starting ID 60. Thread Id: 1
Starting ID 61. Thread Id: 1
Starting ID 62. Thread Id: 1
Starting ID 63. Thread Id: 1
Starting ID 64. Thread Id: 1
Starting ID 65. Thread Id: 1
Starting ID 66. Thread Id: 1
Starting ID 67. Thread Id: 1
Starting ID 68. Thread Id: 1
Starting ID 69. Thread Id: 1
Starting ID 70. Thread Id: 1
Starting ID 71. Thread Id: 1
Starting ID 72. Thread Id: 1
Starting ID 73. Thread Id: 1
Starting ID 74. Thread Id: 1
Starting ID 75. Thread Id: 1
Starting ID 76. Thread Id: 1
Starting ID 77. Thread Id: 1
Starting ID 78. Thread Id: 1
Starting ID 79. Thread Id: 1
Starting ID 80. Thread Id: 1
Starting ID 81. Thread Id: 1
Starting ID 82. Thread Id: 1
Starting ID 83. Thread Id: 1
Starting ID 84. Thread Id: 1
Starting ID 85. Thread Id: 1
Starting ID 86. Thread Id: 1
Starting ID 87. Thread Id: 1
Starting ID 88. Thread Id: 1
Starting ID 89. Thread Id: 1
Starting ID 90. Thread Id: 1
Starting ID 91. Thread Id: 1
Starting ID 92. Thread Id: 1
Starting ID 93. Thread Id: 1
Starting ID 94. Thread Id: 1
Starting ID 95. Thread Id: 1
Starting ID 96. Thread Id: 1
Starting ID 97. Thread Id: 1
Starting ID 98. Thread Id: 1
Starting ID 99. Thread Id: 1
Starting ID 100. Thread Id: 1
Completing ID 3. Response status code is OK. Thread Id: 14
Completing ID 10. Response status code is OK. Thread Id: 8
Completing ID 8. Response status code is OK. Thread Id: 9
Completing ID 7. Response status code is OK. Thread Id: 15
Completing ID 11. Response status code is OK. Thread Id: 14
Completing ID 4. Response status code is OK. Thread Id: 8
Completing ID 9. Response status code is OK. Thread Id: 9
Completing ID 12. Response status code is OK. Thread Id: 8
Completing ID 13. Response status code is OK. Thread Id: 8
Completing ID 6. Response status code is OK. Thread Id: 8
Completing ID 17. Response status code is OK. Thread Id: 8
Completing ID 18. Response status code is OK. Thread Id: 8
Completing ID 21. Response status code is OK. Thread Id: 8
Completing ID 24. Response status code is OK. Thread Id: 8
Completing ID 20. Response status code is OK. Thread Id: 8
Completing ID 30. Response status code is OK. Thread Id: 8
Completing ID 22. Response status code is OK. Thread Id: 8
Completing ID 34. Response status code is OK. Thread Id: 8
Completing ID 32. Response status code is OK. Thread Id: 9
Completing ID 33. Response status code is OK. Thread Id: 9
Completing ID 39. Response status code is OK. Thread Id: 9
Completing ID 35. Response status code is OK. Thread Id: 8
Completing ID 2. Response status code is OK. Thread Id: 9
Completing ID 44. Response status code is OK. Thread Id: 9
Completing ID 23. Response status code is OK. Thread Id: 8
Completing ID 31. Response status code is OK. Thread Id: 14
Completing ID 38. Response status code is OK. Thread Id: 14
Completing ID 43. Response status code is OK. Thread Id: 8
Completing ID 50. Response status code is OK. Thread Id: 9
Completing ID 1. Response status code is OK. Thread Id: 15
Completing ID 48. Response status code is OK. Thread Id: 14
Completing ID 27. Response status code is OK. Thread Id: 8
Completing ID 49. Response status code is OK. Thread Id: 9
Completing ID 28. Response status code is OK. Thread Id: 15
Completing ID 14. Response status code is OK. Thread Id: 14
Completing ID 29. Response status code is OK. Thread Id: 8
Completing ID 26. Response status code is OK. Thread Id: 14
Completing ID 15. Response status code is OK. Thread Id: 9
Completing ID 19. Response status code is OK. Thread Id: 8
Completing ID 25. Response status code is OK. Thread Id: 15
Completing ID 5. Response status code is OK. Thread Id: 14
Completing ID 40. Response status code is OK. Thread Id: 9
Completing ID 60. Response status code is OK. Thread Id: 8
Completing ID 37. Response status code is OK. Thread Id: 15
Completing ID 41. Response status code is OK. Thread Id: 14
Completing ID 16. Response status code is OK. Thread Id: 9
Completing ID 63. Response status code is OK. Thread Id: 14
Completing ID 36. Response status code is OK. Thread Id: 14
Completing ID 42. Response status code is OK. Thread Id: 9
Completing ID 45. Response status code is OK. Thread Id: 14
Completing ID 64. Response status code is OK. Thread Id: 14
Completing ID 53. Response status code is OK. Thread Id: 9
Completing ID 61. Response status code is OK. Thread Id: 15
Completing ID 52. Response status code is OK. Thread Id: 14
Completing ID 67. Response status code is OK. Thread Id: 14
Completing ID 74. Response status code is OK. Thread Id: 14
Completing ID 75. Response status code is OK. Thread Id: 14
Completing ID 62. Response status code is OK. Thread Id: 15
Completing ID 78. Response status code is OK. Thread Id: 8
Completing ID 66. Response status code is OK. Thread Id: 15
Completing ID 55. Response status code is OK. Thread Id: 14
Completing ID 83. Response status code is OK. Thread Id: 15
Completing ID 59. Response status code is OK. Thread Id: 14
Completing ID 68. Response status code is OK. Thread Id: 8
Completing ID 85. Response status code is OK. Thread Id: 15
Completing ID 47. Response status code is OK. Thread Id: 9
Completing ID 72. Response status code is OK. Thread Id: 14
Completing ID 65. Response status code is OK. Thread Id: 8
Completing ID 84. Response status code is OK. Thread Id: 8
Completing ID 70. Response status code is OK. Thread Id: 14
Completing ID 87. Response status code is OK. Thread Id: 14
Completing ID 56. Response status code is OK. Thread Id: 8
Completing ID 90. Response status code is OK. Thread Id: 15
Completing ID 76. Response status code is OK. Thread Id: 9
Completing ID 73. Response status code is OK. Thread Id: 14
Completing ID 69. Response status code is OK. Thread Id: 8
Completing ID 86. Response status code is OK. Thread Id: 15
Completing ID 81. Response status code is OK. Thread Id: 9
Completing ID 91. Response status code is OK. Thread Id: 15
Completing ID 77. Response status code is OK. Thread Id: 9
Completing ID 57. Response status code is OK. Thread Id: 15
Completing ID 98. Response status code is OK. Thread Id: 9
Completing ID 100. Response status code is OK. Thread Id: 15
Completing ID 79. Response status code is OK. Thread Id: 9
Completing ID 58. Response status code is OK. Thread Id: 15
Completing ID 80. Response status code is OK. Thread Id: 8
Completing ID 82. Response status code is OK. Thread Id: 14
Completing ID 89. Response status code is OK. Thread Id: 9
Completing ID 88. Response status code is OK. Thread Id: 15
Completing ID 97. Response status code is OK. Thread Id: 8
Completing ID 51. Response status code is OK. Thread Id: 14
Completing ID 94. Response status code is OK. Thread Id: 9
Completing ID 93. Response status code is OK. Thread Id: 15
Completing ID 71. Response status code is OK. Thread Id: 8
Completing ID 46. Response status code is OK. Thread Id: 14
Completing ID 54. Response status code is OK. Thread Id: 9
Completing ID 95. Response status code is OK. Thread Id: 15
Completing ID 92. Response status code is OK. Thread Id: 8
Completing ID 99. Response status code is OK. Thread Id: 14
Completing ID 96. Response status code is OK. Thread Id: 9
00:00:06.7737961

C:\Program Files\dotnet\dotnet.exe (process 296) exited with code 0.
To automatically close the console when debugging stops, enable Tools->Options->Debugging->Automatically close the console when debugging stops.
Press any key to close this window . . .

我相信 23 秒 vs 7 秒足以证明这是异步/等待场景。

【讨论】:

  • #1 - DbContext 实例是轻量级的,由数据库连接池支持。没有真正的内存压力(任何时候每个 CPU 核心可能只有一个,而且与 Web IO 相比,它们的分配和处置成本相当低)。 #2 - 是的,但是如我的回答中所示,并发级别可能会受到限制。 #3 - 有多个线程。他们不会互相阻挡。您可能会将此与 async/await 混淆。 #4 - 是的,但他们会的。请参阅我上面的评论。
  • 在我的答案中添加了示例代码,以证明线程同时运行。
  • @EricJ.:我添加了我的解决方案来证明它是异步/等待方案并且性能提升非常显着。
  • 正如我所说,async/await 可能完全适合长时间运行的方法体。您构建的测试导致执行的并发线程多于 Parallel 选择使用的线程。这就是它更快的原因(但如果您连接到 localhost 以外的其他东西,差异会更小)。您要求运行时为每个 ID 启动一项任务。在 OP 实际情况下,手动启动任务可能会或可能不会比 Parallel 更好。
  • @EricJ.:我根据 OP 中提供的最小上下文来回答。我只假设ExecuteLongRunningProcessThatReadsDbAndCreatesSomeNewRecords() 方法确实是长期运行的方法。最后,由 Casey Chester 来评估我们的答案并选择最适合他的方案。就是这样。
猜你喜欢
  • 2021-05-27
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多