我有一段代码,其中多个线程使用来自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个线程都将从数据库访问记录,并将失败的调用计为10次,而不是1 ..:/
因此,我的问题是如何防止所有线程进行数据库记录的更新,而是只允许一个线程访问数据库,而是更新记录(通过+1添加失败的调用)? / p>
我该如何实现?
答案 0 :(得分:2)
如果发生错误,您似乎只需要更新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;
}
});
答案 1 :(得分:1)
您处于并行循环中,因此最可能的行为是10个线程中的每个线程都将触发,尝试使用过期的密钥连接到您的API,然后全部失败,并引发异常。
有两种合理的解决方案:
可以使循环中的第一次运行不按顺序进行吗?例如:
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 or E1101 when accessed.
generated-members=REQUEST,acl_users,aq_parent,pandas.*
如果密钥在使用前可能已经过期,但在使用时将处于活动状态,则可以很好地工作。
如果该密钥在启动时可能有效,但在使用时可能过期,则您可能希望尝试捕获该故障,然后在末尾进行记录。
var ids = new ConcurrentBag<string>();
var apiKey = ctx.ApiKey.FirstOrDefault();
bool expired = true;
try {
// Perform API calls
expired = false;
}
catch(Exception ex) {
// log to database once
}
// Or grab another, newer key?
if (!expired)
{
Parallel.ForEach(ids.Skip(1), id =>
{
// Perform API Calls
}
}