首页 > 解决方案 > Parallel.ForEach 循环只允许一个线程访问 C# 中的数据库

问题描述

我有一段代码,其中多个线程使用来自 ConcurrentBag 类型字符串的共享 ID 属性进行访问,如下所示:

var ids = new ConcurrentBag<string>();
// List contains lets say 10 ID's 
var apiKey = ctx.ApiKey.FirstOrDefault();

Parallel.ForEach(ids, id => 
{
    try
    {
        // Perform API calls
    }
    catch (Exception ex)
    {
        if (ex.Message == "Expired")
        {
            // the idea is that if that only one thread can access the DB record to update it, not multiple ones
            using (var ctx = new MyEntities())
            {
                var findApi= ctx.ApiKeys.Find(apiKey.RecordId);
                findApi.Expired = DateTime.Now.AddHours(1);
                findApi.FailedCalls += 1;
            }
        }
    }

});

因此,在这种情况下,如果我有一个包含 10 个 ID 和 1 个密钥的列表用于 API 调用,一旦密钥达到每小时调用限制,我将从 API 捕获异常,然后将密钥标记为用于接下来的一个小时。

但是,在我上面粘贴的代码中,所有 10 个线程都将从 DB 访问记录并将失败的调用计为 10 次,而不是只有 1..:/

所以我的问题是如何防止所有线程更新数据库记录,而是只允许一个线程访问数据库,更新记录(通过+1添加失败的调用)?

我怎样才能做到这一点?

标签: c#asp.netasp.net-mvcmultithreadingc#-4.0

解决方案


如果发生错误,您似乎只需要更新一次 apiKey.RecordId ,为什么不跟踪发生错误的事实并在最后更新一次呢?例如

var ids = new ConcurrentBag<string>();
// List contains lets say 10 ID's 
var apiKey = ctx.ApiKey.FirstOrDefault();
var expired = false;

Parallel.ForEach(ids, id => 
{
    try
    {
        // Perform API calls
    }
    catch (Exception ex)
    {
        if (ex.Message == "Expired")
        {
           expired = true;
        }
    }
}

if (expired)
{
   // the idea is that if that only one thread can access the DB record to 
   // update it, not multiple ones
   using (var ctx = new MyEntities())
   {
     var findApi= ctx.ApiKeys.Find(apiKey.RecordId);
     findApi.Expired = DateTime.Now.AddHours(1);
     findApi.FailedCalls += 1;
    }
});

推荐阅读