C# "shared pointer" for alternative memory management?
I am looking for a way to do this in C#:
- an Asker object will ask a Giver object for Resource objects.
- when asked, Giver will search it's Dictionary for existing matching Resource. If found, it will return the reference of the Resource; otherwise, it will create a the new Resource from database data, save that reference in Dictionary, and finally return the reference.
- the Asker may ask for the same Resource more than once, in which case Giver will return the same Resource from the Dictionary the same number of times.
the Asker may at any time have no use for a given Resource, in which case, it does nothing further to the Resource.
Problem: How can Giver detect for any Resource that it is no longer in use and remove it from the Dictionary? Preferably, Giver should do this without Asker's help.
Is this possible? I can't seem to solve this.
EDIT: Thanks everyone for the great replies. Especially the WeakReferences. I didn't know they were there. But I have 2 main objectives which I could have specified clearer.
- Giver should not rely on Asker to get notified.
- While a given Resource is in use, all of the references must be pointing to the same Resource so that modification to the Resource is reflected in all places where the same Resource is us开发者_Go百科ed.
EDIT: [Removed incorrect code block]
You could store a WeakReference to the resource in the dictionary instead of the resource itself. This wouldn't prevent the resource from being garbage collected.
When you fetched a value (the weak reference) from the dictionary, you'd then need to fetch the Target
and see whether it's null. If it is, the resource has been garbage collected and you'll need to recreate it. Otherwise, you can return the target as the cached resource.
I don't believe there's much control over how "important" a WeakReference
is deemed to be - I don't think you can say that it should make it as far as gen2, for example. Of course you could have some other data structure to make sure that any resource was cached for at least (say) 5 minutes by keeping a strong reference to it for that long.
If you do go for this approach, you may also want to periodically go through the dictionary and clear out entries for which the target has already been garbage collected, to avoid your dictionary getting full of useless entries. If the set of keys is fixed and not too huge, this may not be worth it though, particularly bearing in mind the synchronization you'd probably need.
All this functionality is encapsulated in System.Web.Caching.Cache
.
This can be safely used outside ASP.NET and has mechanism for expiration, reloading ...
To start, what you're talking about is the basic idea behind the IDisposable
interface: a deterministic way for resources to be released. While its main usage is when interacting with unmanaged resources that require explicit release (or interacting with objects that do that), its usage is not restricted to that.
Unfortunately, it fails your last requirement: since it's deterministic, it has to be called by somebody. This somebody would have to be the Asker.
The only solution that I can come up with would be using the WeakReference
class in your Giver
object. This allows you to maintain a reference to an instance that doesn't prevent it from being garbage collected (after it's collected, your reference becomes null
).
Unfortunately, this isn't deterministic. Your reference will become null
(and IsAlive
will be false
) after the object is actually collected, which is not guaranteed to happen at any particular time (or at all during the lifetime of your application).
With those caveats in mind, you could something like this:
public class Giver
{
private Dictionary<string, WeakReference> cache =
new Dictionary<string, WeakReference>();
public object GetResource(string resourceName)
{
WeakReference output;
object returnValue = null;
if(cache.TryGetValue(resourceName, out output))
{
if(output.IsAlive) returnValue = output.Target;
if(returnValue == null) cache.Remove(resourceName);
}
if(returnValue == null)
{
returnValue = ...; // get the actual resource
cache.Add(resourceName, new WeakReference(returnValue));
}
return returnValue;
}
}
The Asker
will have to tell Giver
that it no longer uses a resource.
At this point Giver
should remove the resource.
The Observer
pattern can help here.
You probably need a Dictionary[string,WeakReference]. The MSDN has an example on how to use them to implement more or less the system you are asking for:
http://msdn.microsoft.com/en-us/library/system.weakreference.aspx
Just scroll down to the "Examples" section, where they show how to implement a cache of resources.
WeakReference do not lock objects down, they simply go to null
when the object they are pointing to is reclaimed by the garbage collector.
You can use the WeakReference Class in the Giver
.
One way to do this is with weak references. Here is an article I wrote about that approach a number of years ago:
http://www.devx.com/dotnet/Article/36286
Further to Oded's comments, here's the link to the IObserver interface to get you started:
http://msdn.microsoft.com/en-us/library/dd783449.aspx
精彩评论