【发布时间】:2013-01-15 01:06:05
【问题描述】:
我有一个 SQL Server CLR 存储过程,用于检索大量行,然后执行一个过程并更新另一个表中的计数。
流程如下:
选择 -> 处理 -> 更新计数 -> 将选定的行标记为已处理
该过程的本质是它不应将同一组数据计算两次。 SP 以 GUID 作为参数调用。
因此,我保留了当前正在处理的 GUID 列表(在 SP 中的静态列表中),并暂停执行以相同参数对 SP 的后续调用,直到当前正在处理的处理完成。
当进程在 finally 块中完成但并非每次都有效时,我有代码可以删除 GUID。在某些情况下(例如用户取消 SP 的执行),SP 退出时没有调用 finally 块,也没有从列表中删除 GUID,因此后续调用会无限期地等待。
你们能否给我一个解决方案,以确保无论如何都会调用我的 finally 块或任何其他解决方案,以确保在任何给定时间只有一个 ID 在处理中。
以下是删除处理位的代码示例
[Microsoft.SqlServer.Server.SqlProcedure]
public static void TransformSurvey(Guid PublicationId)
{
AutoResetEvent autoEvent = null;
bool existing = false;
//check if the process is already running for the given Id
//concurrency handler holds a dictionary of publicationIds and AutoresetEvents
lock (ConcurrencyHandler.PublicationIds)
{
existing = ConcurrencyHandler.PublicationIds.TryGetValue(PublicationId, out autoEvent);
if (!existing)
{
//there's no process in progress. so OK to start
autoEvent = new AutoResetEvent(false);
ConcurrencyHandler.PublicationIds.Add(PublicationId, autoEvent);
}
}
if (existing)
{
//wait on the shared object
autoEvent.WaitOne();
lock (ConcurrencyHandler.PublicationIds)
{
ConcurrencyHandler.PublicationIds.Add(PublicationId, autoEvent); //add this again as the exiting thread has removed this from the list
}
}
try
{
// ... do the processing here..........
}
catch (Exception ex)
{
//exception handling
}
finally
{
//remove the pubid
lock (ConcurrencyHandler.PublicationIds)
{
ConcurrencyHandler.PublicationIds.Remove(PublicationId);
autoEvent.Set();
}
}
}
【问题讨论】:
-
我必须看一些代码来说明才能更好地回答这个问题,但是您是否可以创建第二个存储过程(执行第一个)并将 finally 块放在那里?
-
添加了代码示例。是的,但是如果内部 SP 发生粗鲁的中止,我可以确定外部 SP 将正确执行吗?
-
代码示例很有帮助,但我对取消代码如何在 finally 没有执行的情况下工作非常感兴趣。我一直都明白,一旦你在一个 try 块中,finally 总是会被执行。也许有更好的方法来处理取消?
-
我还认为 finally 无论如何都会运行。但后来发现,Finally 只有在一切顺利或线程优雅中止时才会执行。如果存在所谓的粗鲁中止,代码将退出而不去 finally。
-
这是否意味着您正在使用“ThreadAbortException”来“取消”?我不知道您的整体环境的限制,但我会考虑让用户取消操作在表中设置一个标志,并在处理过程中尽可能检查该标志。这是一种常见的做法,也是为什么大多数取消按钮在实际取消进程之前都需要几秒钟的时间。
标签: c# sql-server sqlclr clrstoredprocedure