I am developing some utilities to control threading for a game server and am experimenting with using an IDisposable
"token" so that I can use code like this:
using(SyncToken playerListLock = Area.ReadPlayerList())
{
//some stuff with the player list here
}
The idea being that I acquire a read lock on the list of players in an area and it is automatically unlocked when it goes out of scope with the using block. So far this is all impleme开发者_如何转开发nted and working, but I am worried about the timing of the call to Dispose()
.
Does the SyncLock
variable simply get marked for disposal when the program leaves the using block and then get cleaned up by the Garbage Collector at some point later, or does the current thread execute the Dispose()
method as a part of leaving the using
block?
This pattern is basically RAII, where a lock is the resource being allocated. An example of this pattern (ie, using an IDisposable
"token") has also been used by Jon Skeet in his MiscUtils here
It's cleaned up immediately after the using
scope exits.
In effect, this
using(SyncToken playerListLock = Area.ReadPlayerList())
{
//some stuff with the player list here
}
is syntactic sugar for
IDisposable playerListLock;
try {
playerListLock = Area.ReadPlayerList();
}
finally {
if (playerListLock != null) playerListLock.Dispose();
}
The very purpose of using
is to enable RAII-like functionality in C#, which doesn't feature deterministic destruction.
Dispose
gets called when you exit the scope of the using
. Syntactic sugar for:
MyObj instance = null;
try
{
instance = new MyObj();
}
finally
{
instance.Dispose();
}
Dispose()
is called immediataly when the using
block is exited.
using
is more or less the C# equivalent of the RAII idiom in C++.
The using
docs state that:
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler.
So yes, it is called as soon as you exit the block.
Dispose gets called at the end of the using block, you should implementation Dispose for SyncToken so that the SyncLock can be released deterministically, this is whole point of the Dispose Pattern.
When you leave the scope of the using block the Dispose() function is called releasing the allocated resource, however the SyncToken is collected by the GC whenether it runs.
using
blocks are just a syntactic sugar of a try-finally
block:
using (var myObject = new MyObject())
{
...
}
is equivalent to:
MyObject myObject = null;
try
{
myObject = new MyObject();
...
}
finally
{
if (myObject != null)
{
myObject.Dispose();
}
}
So you have the certainty that Dipose() will be called inmeadiately once you exit the using
block.
An alltogether different matter is when the GC
collects the object. There is no garantee of when that will happen.
The Dispose method is called as soon as your variable goes out of scope, the garbage collector will kick in at some undetermined point later and doesn't affect this.
Also, have a look at this for a simple comparison of what happens underneath: using and IDisposable
精彩评论