我一直在经历albahari线程课程,但是当我自己去实验时,事情没有按计划进行。我不知道为什么以下锁定“_Lock”没有阻止。不知何故,每个SafeLock方法中的锁定也不会阻止其他线程使用其方法。已经有一段时间了,所以任何帮助都会非常感激。
//Here I just call a method 10x
//The method (SL1) then creates a SafeLocks object and runs 3 methods
private void SL1_Btn_Click(object sender, EventArgs e)
{
SL1(); SL1(); SL1(); SL1(); SL1();
SL1(); SL1(); SL1(); SL1(); SL1();
}
private void SL1()
{
console.WriteLine("Thread Enter");
SafeLocks sl = new SafeLocks();
ThreadPool.QueueUserWorkItem(o => sl.ShowVars());
ThreadPool.QueueUserWorkItem(o => sl.SetZero());
ThreadPool.QueueUserWorkItem(o => sl.ShowVars());
console.WriteLine("Thread Exit");
}
//Now for the Thread-"Safe" class
class SafeLocks
{
//private static int staticVar = 1;
private static int staticVar = 1;
public int instanceVar = 1;
private static readonly object _Lock = new object();
public SafeLocks() //sets both to 100,000
{
lock (_Lock)
{
while (staticVar < 99999)
{ staticVar++; }
while (instanceVar < 99999)
{ instanceVar++; }
staticVar++;
instanceVar++;
if (instanceVar != 100000)
{ Console.WriteLine("I1=" + instanceVar.ToString()); }
if (staticVar != 100000)
{ Console.WriteLine(("S1=" + staticVar.ToString())); }
}
}
public void ShowVars()
{
lock (_Lock)
{
if (instanceVar != 100000 && instanceVar != 0)
{ Console.WriteLine("I2=" + instanceVar.ToString()); }
if (staticVar != 100000 && staticVar != 0)
{ Console.WriteLine("S2=" + staticVar.ToString()); }
}
}
public void SetZero()
{
lock (_Lock)
{
while (staticVar > 0)
{
staticVar--;
}
while (instanceVar > 0)
{
instanceVar--;
}
if (instanceVar != 0)
{ Console.WriteLine("I3=" + instanceVar.ToString()); }
if (staticVar != 0)
{ Console.WriteLine("S3=" + staticVar.ToString()); }
}
}
}
}
答案 0 :(得分:2)
问题来自于构造函数的组合以及构造多个实例或调用SetZero
之间没有线程安全的事实。也就是说,您可以在调用SetZero
之前创建两个实例。 (编辑:你现在的锁正常运行,因为没有方法可以同时执行,但它们没有做任何事情来保证调用这些方法的 order 你设置的线程最终会在最终调用SetZero
之前多次调用对象构造函数。)
首先在你的构造函数中(注意,我消除了instanceVar
用法,因为它运行正常):
while (staticVar < 99999)
{ staticVar++; }
staticVar++;
此假设 staticVar
以小于99999
的值开始,然后再增加一个值以使其成为10000
}。但是,如果 p>
通过执行以下操作可以很容易地证明这一点,而无需任何线程:
100000
如果您将构造函数代码更改为:
100001
代码将“正常”,但它仍然技术上可能有一个错误,因为无法保证在创建下一个实例的构造函数之前调用SafeLocks sl = new SafeLocks();
SafeLocks s2 = new SafeLocks(); //outputs "S1=100001"
SafeLocks s3 = new SafeLocks(); //outputs "S1=100002"
SafeLocks s4 = new SafeLocks(); //outputs "S1=100003"
SafeLocks s5 = new SafeLocks(); //outputs "S1=100004"
。我不确定这是否是一个问题,因为这是一个非常人为的设计/示例,但在未来的使用中你需要注意的事情。 (另外,考虑在可能的情况下分离静态和实例级别的线程安全问题)否则,唯一的方法是避免它,即确保在构造另一个之前调用lock (_Lock)
{
while (staticVar <= 99999) //note the <= comparison change
{ staticVar++; }
while (instanceVar <= 99999) //note the <= comparison change
{ instanceVar++; }
if (instanceVar != 100000)
{ Console.WriteLine("I1=" + instanceVar.ToString()); }
if (staticVar != 100000)
{ Console.WriteLine(("S1=" + staticVar.ToString())); }
}
我相信,在您当前的设计中是非平凡的无论如何,一般来说都很尴尬/不明显。