Thanks Pete. It was helpful. I didnt know about Boxing making it atomic.
---
Ajay
"Peter Duniho" <Np*********@nnowslpianmk.comwrote in message
news:op***************@petes-computer.local...
On Thu, 17 Jan 2008 20:23:13 -0800, Ajay Kalra <aj*******@yahoo.comwrote:
I didnt say that we have multiple lock objects. You can use the same
object. In this case, all getter can acquire a readonlylock. For setters,
I would use the same object on which you apply the lock. Why is that
hmm... bad?
Well, not having multiple lock objects but then locking at such a low
level is also bad, but for performance reasons.
It really just depends. Without knowing everything that's going on with
the code, it's hard to say. Sometimes a fine-granularity on your locking
code is no problem. But as Nicholas says, often times you've got a
situation where at that level of granularity you're just going to keep
acquiring the same lock over and over. You're better off in that
situation to just grab the lock once, do everything you need to do, and
then release it.
I am curious to know because we follow this pattern and have had no
issues at all. I would like to know the downside that we are missing.
If you understand the _potential_ for problems and can ensure they are not
an issue here, you should be okay. If all you are ever locking is that
one DateTime property, it's not an issue.
That said, if all you're ever locking is that one DateTime property then
you may not need the lock at all. You can use boxing instead to create an
atomic access:
object _startTime = new DateTime();
public DateTime StartTime
{
get { return (DateTime)_startTime; }
set { _startTime = value; }
}
I did a quick test (code below) and two threads executing 10 million
iterations accessing both the setter and getter of the property are
consistently 30-40% faster using boxing than using a lock. Of course,
even the "slow" case took only 1.1 seconds to do 10 million iterations.
The usual caveat of "most code is going to spend most of its time doing
more interesting things" applies, meaning that even if you could get the
overhead down to zero it might not make a real difference in your
application's performance.
On the other hand, if you are entering and leaving a lock for every little
thing, that might cause that caveat to no longer be applicable. All of
the sudden, performance matters. :)
Pete
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace TestLockPerformance
{
class Program
{
public interface IDateTimeHolder
{
DateTime DateTime
{
get;
set;
}
}
public class LockingHolder : IDateTimeHolder
{
#region IDateTimeHolder Members
private DateTime _dt;
private object _objLock = new object();
public DateTime DateTime
{
get
{
lock (_objLock)
{
return _dt;
}
}
set
{
lock (_objLock)
{
_dt = value;
}
}
}
#endregion
}
public class BoxingHolder : IDateTimeHolder
{
#region IDateTimeHolder Members
private volatile object _dt = new DateTime();
public DateTime DateTime
{
get
{
return (DateTime)_dt;
}
set
{
_dt = value;
}
}
#endregion
}
public class HolderThread
{
private IDateTimeHolder _dth;
private int _iterations;
private Thread _thread;
public HolderThread(IDateTimeHolder dth, int iterations)
{
_dth = dth;
_iterations = iterations;
_thread = new Thread(_ThreadStart);
}
public void Start()
{
_thread.Start();
}
public void Wait()
{
_thread.Join();
}
private void _ThreadStart()
{
while (_iterations-- 0)
{
DateTime dtT = _dth.DateTime;
_dth.DateTime = dtT;
}
}
}
static void Main(string[] args)
{
int iterations = 10000000;
switch (args.Length)
{
case 2:
{
int iT;
if (int.TryParse(args[1], out iT))
{
iterations = iT;
}
}
goto case 1;
case 1:
{
IDateTimeHolder dth;
switch (args[0])
{
case "lock":
dth = new LockingHolder();
break;
case "box":
dth = new BoxingHolder();
break;
default:
Console.WriteLine("first parameter must be
\"lock\" or \"box\"");
dth = null;
break;
}
if (dth != null)
{
HolderThread ht1 = new HolderThread(dth,
iterations),
ht2 = new HolderThread(dth, iterations);
Stopwatch sw = new Stopwatch();
sw.Start();
ht1.Start();
ht2.Start();
ht1.Wait();
ht2.Wait();
sw.Stop();
Console.WriteLine("{0} iterations took {1}",
iterations, sw.Elapsed);
}
}
break;
default:
Console.WriteLine("usage: {0} lock|box [<iteration
count>]", Environment.CommandLine.Split(' ')[0]);
break;
}
}
}
}