为什么APPLOCK_MODE和APPLOCK_TEST在这里返回错误答案?

时间:2017-04-17 15:07:50

标签: sql-server sql-server-2012

要么我做错了(可能),要么APPLOCK_MODEAPPLOCK_TEST有时会回错。

在一个标签中,我运行此代码:

begin tran
  declare @lock int = -1
  exec @lock = sp_getapplock @Resource = 'testlock', @Lockmode = 'Exclusive',
    @LockOwner = 'Transaction', @LockTimeout=0

  if @lock = 0
    print 'got lock'
  else
    print 'failed to get lock' 

  waitfor delay '00:00:05'

  if @lock = 0
    exec sp_releaseapplock @Resource = 'testlock', @LockOwner = 'Transaction'

  print 'released lock'
commit

在另一个标签中,我运行此代码:

begin tran

  print 'APPLOCK_TEST=' + convert(varchar(max), 
      APPLOCK_TEST(suser_name(), 'testlock', 'Exclusive', 'Transaction'))
  print 'APPLOCK_MODE=' + 
      APPLOCK_MODE(suser_name(), 'testlock', 'Transaction')

  declare @lock int

  exec @lock = sp_getapplock @Resource = 'testlock', @Lockmode = 'Exclusive',
      @LockOwner = 'Transaction', @LockTimeout=0

  print 'Result=' + convert(varchar(max), @lock)

  if @lock = 0 -- lock is held
  begin
    print 'releasing lock'
    exec sp_releaseapplock @Resource = 'testlock', @LockOwner = 'Transaction'
  end

commit

在第一个选项卡中运行代码然后在第一个选项卡完成之前在第二个选项卡中运行代码时,我看到的是:

APPLOCK_TEST=1
APPLOCK_MODE=NoLock
Result=-1

1的{​​{1}}和APPLOCK_TEST的{​​{1}}的结果值都表明该锁目前尚未使用,我应该能够获得它。这是错误的,因为锁定仍然由另一个选项卡保持,直到完成为止。实际调用NoLock失败确认了这一点。

让我感到困惑的是,为什么这两个测试函数的回归与我期望的相反。我在这里缺少某些方面吗?

1 个答案:

答案 0 :(得分:1)

您应该将public指定为主体,而不是suser_name(),因为这也是获取锁定的(默认)主体。指定APPLOCK_TEST时(public将返回0(锁定不可授予)。 APPLOCK_MODE仍将返回NoLock,因为它返回当前事务所持有的锁定模式 - 并且当前事务未持有锁定。 (documentation无疑对此不清楚,但确实给出了预期行为的示例。)如果这似乎无用,请考虑多个事务可能持有(可共享)这一事实)锁定在不同的模式。

但是,根据@BenThul的评论,在获取锁定状态之前测试锁定状态是不明智的,因为它的时间点信息在您实际尝试获取锁定时可能无效。很难想象APPLOCK_TEST可以有意义地使用的情况(与sp_getapplock相反,具有零或非常小的超时)。