1

我编写了多线程应用程序,每个线程都调用一个 sql server 过程。我需要保护一行不被多个线程占用我希望每一行只被一个线程占用我使用了这个代码:

CREATE PROCEDURE GetRow
    @ThreadID int
AS
BEGIN

    DECLARE @rowID int;
    SELECT TOP (1) @rowID=[ID] FROM [TestTable] WHERE [ThreadID] = 0 (AND some other parameters)

    IF @rowID is not NULL
    BEGIN
        UPDATE TOP (1) [TestTable] SET [CallCount] = [CallCount]+1,[ThreadID] = @ThreadID WHERE [ID] = @rowID

    --some other codes

    END
END

正如我所说,我运行应用程序,然后我的应用程序创建 10 个线程,我将 threadid 设置为仅由一个线程占用一行,但我看到一行被超过 1 个线程调用,并且大约 4-5 个线程得到一行.. 怎么可能我这样做是最好的方法,我通过这段代码解决了这个问题,我不喜欢在选择和更新中有两个相同的位置:

CREATE PROCEDURE GetRow
    @ThreadID int
AS
BEGIN

    DECLARE @rowID int;

    UPDATE TOP (1) [TestTable] SET [CallCount] = [CallCount]+1,[ThreadID] = @ThreadID WHERE [ThreadID] = 0 (AND some other parameters)


    SELECT TOP (1) @rowID=[ID] FROM [TestTable] WHERE [ThreadID] = @ThreadID (AND some other parameters)

    IF @rowID is not NULL
    BEGIN

    --some other codes

    END
END

我怎么能用顶级代码做到这一点?我应该先更新然后选择上面的代码来修复它吗?

4

2 回答 2

2

如果您使用的是 SQL Server 2005 或更新版本,您可以将and结合UPDATESELECT使用 newOUTPUT子句

但是,我建议备份并考虑您的数据库架构,以及它是否真的适合您的问题——尤其是多线程!

我得到的印象是你想计算来自每个线程的调用。让每个调用INSERT一行到一个ThreadCallCount只有一列的简单表中会更好ThreadID。每个呼叫都是一个新行。稍后您可以通过Sum()此表ThreadID来获取每个线程的总调用次数。这将很简单,很容易确保它是线程安全的。

听起来您正在尝试检索其他一些数据,但尚不清楚它是如何/何时存储的。但是类似的分析——以及更多、更简单的表格——可能会对你有所帮助。

一般来说:不要觉得你需要把所有东西都挤在一张桌子上。使用多线程将有助于将事物分解成更小的部分。随意使用更多表,每个表都有一个明确的目的。SQL Server 旨在安全有效地处理行事务,因此请尽量利用它。

于 2013-07-13T15:46:23.300 回答
1

我建议编写安全代码而不是在存储过程中这样做。

您没有提及您的编码语言,在 C# 中,一种方法是将调用此存储过程的代码包装在lock块内:

using System.Threading
...

public class ClassName {

    object syncLock = new object();
    ...

    SomeFunction(){

        lock(syncLock){

            // For example, com could be SqlCommand object that calls stored procedure
            int updatedRowCount = com.ExecuteNonQuery();
            ...
        }
    }

}
于 2013-07-13T14:16:24.483 回答