服务操作MyMethod(int id)
从单个数据库表中检索特定行(基于id
参数),在它返回之前,它还将该状态保存回表中。如果两次通话(第一次通话发生在交易中 T1 ,而第二次发生在交易 T2 内)同时发送到MyMethod()
,则服务将尝试执行同时打两个电话。由于 T1 和 T2 都尝试访问相同的数据库表,因此两个事务中的一个将被授予对资源的访问权限,而其他事务将被阻止,直到原始事务提交或中止。但相反,我得到一个异常事务(进程ID 54)在锁资源上与另一个进程死锁,并被选为死锁受害者
我不明白抛出死锁异常的原因,因为据我所知,没有任何死锁的危险。首先,在不同的行上访问和操作这两个事务。为什么在原始事务提交或中止之前,DB资源只是被锁定了?!
以下是代码:
[ServiceContract]
public interface IService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void Process(int id);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults=true)]
public class Service : IService
{
string state_Data = "";
[OperationBehavior(TransactionScopeRequired = true)]
public void Process(int id)
{
GetState(id);
Thread.Sleep(6000);
SaveState(id);
}
private void GetState(int id)
{
using (SqlConnection con = new SqlConnection())
{
con.ConnectionString = "data source=localhost; initial catalog=WCF; integrated security=sspi;";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "SELECT * FROM StateTable WHERE id = @id";
cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;
cmd.Connection = con;
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
state_Data = reader["State"].ToString();
}
}
private bool SaveState(int id)
{
using (SqlConnection con = new SqlConnection())
{
con.ConnectionString = "data source = localhost; initial catalog=WCF; integrated security=sspi;";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "UPDATE StateTable SET State=@State WHERE Id = @id";
cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;
cmd.Parameters.Add("@State", SqlDbType.NVarChar).Value = state_Data;
cmd.Connection = con;
con.Open();
int ret = cmd.ExecuteNonQuery();
return ret == 1;
}
}
}
修改
如果这有帮助,这里是客户端代码:
第一位客户:
ServiceClient proxy = new ServiceClient("WSDualHttpBinding_IService");
using (TransactionScope scope = new TransactionScope())
{
proxy.Process(1);
scope.Complete();
}
第二位客户:
ServiceClient proxy = new ServiceClient("WSDualHttpBinding_IService");
using (TransactionScope scope = new TransactionScope())
{
proxy.Process(2);
scope.Complete();
}
谢谢
答案 0 :(得分:4)
实际上这段代码是保证死锁。所有成功的GetState
调用都可以在同一个ID
上成功完成,因为它们都获取(并且由于可序列化的事务范围而保留)共享锁,因此是兼容的。对SaveState
的任何后续尝试都将阻止,因为大量共享锁都与更新所需的X锁不兼容。下一个SaveState
将陷入僵局。 100%repro,保证,每次。
如果您关心效果,则应使用optimistic concurrency。如果性能无关紧要,那么GetState
应该专门锁定状态,例如。通过提供XLOCK提示。
当然,我假设StateTable中的ID
上有一个聚簇索引。