lockmanager.zip
Free
bin
LockManager.csproj.user
obj
Step1
App.ico
bin
obj
Step1.csproj.user
Step2
App.ico
bin
obj
Step2.csproj.user
Step3
App.ico
bin
obj
Step3.csproj.user
Step4
App.ico
bin
obj
Step4.csproj.user
Step5
App.ico
bin
obj
Step5.csproj.user
ThreeObjectsTest
App.ico
bin
obj
ThreeObjectsTest.csproj.user
|
/// <disclaimer>
/// This software is protected by your own conscience (c).
/// You can use it however you want and wherever you want.
/// You are allowed to change this code, copy this code, or delete this code.
/// You can buy this code; sell this code; present this code to your mom on her birthday.
/// You can replace authors name with your own. You also can replace all the code with your own leaving
/// only authors name.
/// The only thing you cannot do, is to violate this license agreement. You simply are not able to.
/// </disclaimer>
/// <author>
/// Sergei Zotin
/// szotin@shaw.ca
/// Burnaby, BC, Canada
/// Feel free to contact me for bug reports, feature requests and contract offers.
/// </author>
/// <version>1.6</version>
using System;
using System.Threading;
using ZEN.Threading;
using System.IO;
class A
{
private B b;
private int x;
private int y;
// NEW: By using private lock object instead of this, we guarantee that users of A will not
// NEW: take part in our synchization game.
// NEW: That allow us to make some assumptions about a state of locks.
private string lockObject = "LockA";
public B TheB
{
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
get
{
lock ( lockObject )
{
return b;
}
}
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
set
{
lock ( lockObject )
{
b = value;
}
}
}
public int X
{
get
{
LockManager.Lock ( lockObject );
try
{
return x;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
// NEW: 1. We can change the order of assignments to avoud undoing. The idea is: just not to set x before b.X
// NEW: is set.
// NEW: 2. Now we use private lock object, so we can be sure that external components do not lock either A or B.
// NEW: At the same time we never use X internally, so when it is called, either A or B are never locked. This
// NEW: means that even if LockManager.LockCount is not 1, we can fix the deadlock right here. External locks
// NEW: don't take part in it. LockManager.LockCount is a time consuming operation (it makes some loops throw
// NEW: internal LockManager arrays, which themselves are synchronized). Avoiding it definitely improves
// NEW: performance.
set
{
bool retry;
do
{
retry = false;
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
b.X = value;
x = value;
}
catch ( DeadlockException )
{
retry = true;
}
finally
{
LockManager.Unlock ( lockObject );
}
} while ( retry );
}
}
public int Y
{
get
{
LockManager.Lock ( lockObject );
try
{
return y;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
set
{
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
y = value;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
}
}
class B
{
private A a;
private int x;
private int y;
// NEW: By using private lock object instead of this, we guarantee that users of A will not
// NEW: take part in our synchization game.
// NEW: That allow us to make some assumptions about a state of locks.
private string lockObject = "LockB";
public A TheA
{
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
get
{
lock ( lockObject )
{
return a;
}
}
// NEW: It is never involved in cascade locks (we can be sure, because lock object is now private),
// NEW: so deadlock cannot be a problem here.
// NEW: We can use lock(lockObject) instead of LockManager.Lock(lockObject), which works much faster.
set
{
lock ( lockObject )
{
a = value;
}
}
}
public int X
{
get
{
LockManager.Lock ( lockObject );
try
{
return x;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
set
{
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
x = value;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
}
public int Y
{
get
{
LockManager.Lock ( lockObject );
try
{
return y;
}
finally
{
LockManager.Unlock ( lockObject );
}
}
// NEW: 1. We can change the order of assignments to avoud undoing. The idea is: just not to set y before a.Y
// NEW: is set.
// NEW: 2. Now we use private lock object, so we can be sure that external components do not lock either A or B.
// NEW: At the same time we never use X internally, so when it is called, either A or B are never locked. This
// NEW: means that even if LockManager.LockCount is not 1, we can fix the deadlock right here. External locks
// NEW: don't take part in it. LockManager.LockCount is a time consuming operation (it makes some loops throw
// NEW: internal LockManager arrays, which themselves are synchronized). Avoiding it definitely improves
// NEW: performance.
set
{
bool retry;
do
{
retry = false;
LockManager.Lock ( lockObject );
try
{
Thread.Sleep ( 10 ); // just to increase the odds of a deadlock
a.Y = value;
y = value;
}
catch ( DeadlockException )
{
retry = true;
}
finally
{
LockManager.Unlock ( lockObject );
}
} while ( retry );
}
}
}
class Step4
{
private A a;
private B b;
private Step4 ( A _a, B _b )
{
a = _a;
b = _b;
a.TheB = b;
b.TheA = a;
}
private void RunX ()
{
for ( int i = 0; i < 20; i++ )
{
a.X = i;
System.Console.WriteLine ( "Thread X: a.X={0}; b.X={1}", a.X, b.X );
}
}
private void RunY ()
{
for ( int i = 0; i < 20; i++ )
{
b.Y = i;
System.Console.WriteLine ( "Thread Y: a.Y={0}; b.Y={1}", a.Y, b.Y );
}
}
// NEW: We know the deadlocks are there. We can save some time by not logging them.
// NEW: So we don't turn the log on.
[STAThread]
static void Main ( string[] args )
{
A a = new A();
B b = new B();
Step4 step4 = new Step4 ( a, b );
Thread threadX = new Thread ( new ThreadStart ( step4.RunX ) );
Thread threadY = new Thread ( new ThreadStart ( step4.RunY ) );
threadX.Name = "X";
threadY.Name = "Y";
threadX.Start ();
threadY.Start ();
threadX.Join ();
threadY.Join ();
System.Console.WriteLine ( "Press Enter..." );
System.Console.In.Read();
System.Environment.Exit ( 0 );
}
}
|
By viewing downloads associated with this article you agree to the Terms of use and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.