Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Singletons and garbage collection 1

Status
Not open for further replies.

BobRodes

Instructor
May 28, 2003
4,215
0
0
US
I'm working on putting together a singleton class, and I'd like to make sure that the standard managed code garbage collection model works efficiently here.

Would there be circumstances under which I could not rely on standard garbage collection with a singleton object? First, the object contains a reference to itself after the first instantiation. When it's no longer in use by any client, it still has that reference, doesn't it? So does that mean it doesn't get disposed, unless I specifically do something like keep track of external references and manually dispose when there are none?

Second, if the singleton object is using resources, does it have to specifically release references to those resources in order for garbage collection to work properly on those resources? I'm thinking that the answer is no, that as GC goes about its business it looks for references to objects that are no longer there and treats them as invalid. Is that the case? Further with regard to using resources, is there some sort of default timeout period in GC where if an object is inactive for a while it will be destroyed along with any object references it has open?

Any other advice about singletons and memory management in C#?

TIA

Bob
 
1st question... do you require a singleton? Does your system require exactly one instance of that object? if the answer to either of these is no, then why create a singleton?

btw: preformance doesn't count as a valid reason until preformance is a problem.

singletons exist from the time they are instantiated to the time they are disposed like any other object.

usually the object which manages the singleton, is not the object which is a singleton. that's too much responsibility for one object.

most IoC (inversion of control) containers will mange object scope for you. Unity, StructureMap, Windsor all take care of this.

there are a couple different ways to manage singletons. the simplest is
Code:
public class Foo
{
   public static readonly Foo instance = new Foo();

   //to ensure clients cannot create new instances.
   private Foo()
   {
   }
}
now this goes against what I said before about the object managing itself. but this is the simplest way.

if you lazy load the instantiation where it's not created until you request it. the you get into locking and stuff. I think it looks something like this.

Code:
public class Foo
{
   private static readonly object blah = new object();
   private static readonly Foo instance = null;

   public Foo Instance
   {
     get
     {
        if(instance == null)
        {
            lock(blah)
            {
               if(instance == null)
               {
                  instance = new Foo();
               }
            }
        }
        return instance;
     }
   }

   //to ensure clients cannot create new instances.
   private Foo()
   {
   }
}
this is the basis of how IoC containers work when creating singletons.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
I suppose this would fall under "Other Advice". When using a singleton, I've come to rely pretty heavily on the generic singleton provider detailed in this code project article:

This is a pretty simple way to ensure that your objects don't need to maintain their singleton-ness.


[small]----signature below----[/small]
The author of the monograph, a native of Schenectady, New York, was said by some to have had the highest I.Q. of all the war criminals who were made to face a death by hanging. So it goes.

My Crummy Web Page
 
Hi Guys,

Thanks for all the information. Jason, I recognize that a singleton, being the most intellectually accessible design pattern, has a way of becoming the hammer that makes everything look like a nail. To answer your question, I came across a job requisition that had a "pre-qualification exercise" in C#, one of the requirements of which was to build a singleton. Being out of work, I'm doing the exercise to show off my C# skills. Well, to develop the skills and show them off at the same time, to be more honest. :)

Alex, I came across the Nested concept less well explained somewhere else, so I appreciate the link. I've also worked on the lock thing, Jason. I gave up last night when I kept getting an error in the Console application that said that my class had no constructor, when it really, really did. I'm going to break that apart today; if I have a problem with it I'll start a new thread.

Now. The question that I was asking was related to memory management in a singleton. The reason that I asked it was that one of the requirements of the exercise was to "write the class so that it is thread safe and releases memory properly." My present understanding is that the best way to release memory properly is to do nothing and leave it for GC. (After all, isn't that what "managing" managed code is about?) However, I had some concerns about the singleton pattern in this regard, which I have detailed in the original post. Do either of you, or anyone else, have insights into this that I would need to have as well?

Thanks again,

Bob
 
I'm sorry Jason, you did answer my question. I didn't understand that on first read.
 
Jason, one thing about your answer. When you say "manage object scope", that means to me which objects can see it, and is a function of whether it is public, protected, private or whatever. Are you using the term scope to mean what I would call object lifetime, which has to do with when the object is instantiated and destroyed?
 
bob, spiking code is a great way to hone your programming skills, and the only way we get better at it. I questioned your original post because a lot of devs (myself included) have found shinny new patterns to incorporate into projects just because they can, not because it's required.

When you say "manage object scope", that means to me which objects can see it, and is a function of whether it is public, protected, private or whatever.
this is accessibility (private, public, internal, etc.) who can see the object/members.

Are you using the term scope to mean what I would call object lifetime, which has to do with when the object is instantiated and destroyed
yes, how long is the instance of the object available.

I know when working with object scope (singleton or otherwise) and GC things like object reference and event registration need to be taken into account.

for example if we have 2 objects: notifier and receiver. receiver is instantiated and subscribes to a notifier event. notifier will not be fully GCed until reciever has unsubscribed to notifiers event. otherwise you have orphaned events or something like that in memory.

this biggest areas where I have seen this is win/web form guis with manually registered event handlers. and messaging frameworks like NServiceBus, RhinoServiceBus and MassTransit.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Got you. Thanks for your help. I got all the code working, by the way. :)
 
cool, could you post the relevant snippets so others can learn from your experience.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Sure enough. I'm writing some notes and will do so when I'm done.
 
Here we are:


Class code
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using DataExport;  //We're going to self-create an instance, so we're using our own type library

//This class takes input line by line from clients, stores it locally in a list object, and when the export method
//is called persists (saves) the data in an XML file.  The class is set up as a Singleton (only one instance of
//the class may exist at one time), and is also set up to be thread-safe.  

namespace DataExport
{
    interface iDataMethods
    {
        int Export(string XMLFilePath);
        int AddRecord(string RecordText);
        bool AllowExport  //just for test purposes, doesn't make much sense for a Data exporter to not allow 
                          //exporting!  The main purpose of this is to test thread safety, by seeing if you can get
                          //one thread to cause the other not to run the export.  I basically wound up not using it.
        {
            get;
            set;
        }
    }

    public class DataExporter : iDataMethods  //implements the iDataMethods interface
    {
        private List<string> addedrecords = new List<string>();  //generic List object, will hold a single
        //string field of data.  In real life, this would either be an object, a struct, or perhaps a 
        //semicolon-delimited string that could be parsed into multiple fields.
        private bool exportallowed;
        private static DataExporter instance = null;  //Holds a private reference to the singleton instance.
        private static readonly object locker = new object();  //Used to lock a block of code

        private DataExporter() { }  //overriding the default constructor to make it private.  Private constructor 
        //requires instantiation via a method or property, in this case the Instance property.

        public static DataExporter Instance  //self-reference
        {
            get
            {
                lock (locker) //locking makes the code in the brackets execute atomically.  This code is the basic
                //singleton pattern: check for an existing instance, if not found create it, return result.
                {
                    if (null == instance)
                    {
                        instance = new DataExporter(); //self-reference
                    }
                    return instance;
                }
            }
        }

        public bool AllowExport
        {
            get { return exportallowed; }
            set { exportallowed = value; }
        }

        //Export transfers the present contents of the List object (addedrecords) into a DataSet (ds), writes
        //the result to an XML file, and clears the List object.
        public int Export(string XMLFilePath) 
        {
            if (!exportallowed) { return 0; }
            try
            {
                DataSet ds = new DataSet();
                ds.Tables.Add();
                ds.Tables[0].Columns.Add("TheField", typeof(string));
                foreach (string ar in addedrecords)
                {
                    ds.Tables[0].Rows.Add(ar);
                }
                ds.WriteXml(XMLFilePath);
                addedrecords.Clear();
                return 1;
            }
            catch
            {
                return 0;
            }
        }

        public int AddRecord(string RecordText)
        {
            try
            {
                addedrecords.Add(RecordText);
                return 1;
            }
            catch
            {
                return 0;
            }
        }
    }
}

Console Application code
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using DataExport;

namespace ConsoleClient
{
    class Program
    {
        static void Main(string[] args)
        //creating two threads that use the DataExporter singleton, to test for thread safety.  The parameters
        //"1" and "2" carry over into the XML files.
        {
            Thread t1 = new Thread(new ParameterizedThreadStart(doStuff));
            Thread t2 = new Thread(new ParameterizedThreadStart(doStuff));

            t1.Start("1");
            t2.Start("2");
        }

        static void doStuff(Object tPar)
        {
            int eVal;
            DataExporter de = DataExporter.Instance;

            de.AllowExport = true;
            lock (de) //Locking the block ensures thread safety.  Removing the lock causes all of the line items
            //in both threads to be written to each of the two xml files, when we want one thread for each file.
            {
                for (int i = 1; i <= 20; i++)
                {
                    eVal = de.AddRecord("Thread " + tPar + "Record " + Convert.ToString(i));
                    if (0 == eVal)
                    {
                        Console.WriteLine("AddRecord didn't work");
                        return;
                    }
                    Thread.Sleep(100);
                }
            } //not including the Export method in the lock block is an experiment in granularity.  It would seem
            //possible that while it's dumping the List object to the DataSet object, the other thread could be 
            //populating the list object, with resulting pollution.  However, I've been unable to recreate that
            //scenario here.  Perhaps with lots of records and lots of threads it could happen.  If so, including 
            //the rest of the code in the lock block would solve the problem.
            eVal = de.Export("c:\\Temp\\" + "myFile" + tPar + ".xml");
            if (0 == eVal)
            {
                Console.WriteLine("Export didn't work");
                return;
            }
        }
    }
}
 
Update:

I added the following:

Code:
                    Console.WriteLine("Export to " + fileName + " Successful.");
                }
                Console.WriteLine("strike a key when ready...");
                Console.ReadKey(true);
and sure enough, the first line of the second file got stuck at the end of the first file instead, indicating the failure of my experiment. Enclosing the rest of the code in the lock block fixed the problem.

Now, this strikes me as not at all scalable, owing to the large granularity of the lock block. Perhaps someone would wish to share some ideas of how to make it more scalable.

Bob
 
and sure enough, the first line of the second file got stuck at the end of the first file instead, indicating the failure of my experiment.
this makes sense. your have a singleton of the data exporter. then you create 2 threads which add a record and export. The data exporter has state because of the private list class which holds the data to export.

if you want separate threads to produce unique instances of the exported files, then you need a singleton per thread. This can be done using using the thread static attribute.
here is an example

now you could also have a singleton of DataExporter and have another object manage the instances of data per thread. now the data exporter does not have state.


Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Thanks for this, Jason. Now, do you feel that it would be a better solution to the problem at hand to have a singleton per thread? I don't see in this case where there would be more than one reference to the DataExporter object per thread anyway, which would seem to make the singleton unnecessary. So, are you saying that having a separate instance for each thread is better, or that it's just another approach to ensuring thread safety?
 
just another approach.

Right now data exporter has state because of the IList<> of data which is stored before calling export. I would find a way to refactor DataExporter so that it did not keep state. Then the DataExporter would be free from side effects whether it's a singleton or not.

taking a step back. DataExporter could represent a unit of work. durning the scope of for an given UOW you would add records, and when finished export. In this case DataExporter should be transient.

If your familar with NHibernate or ActiveRecord they use the ISession interface as a UOW. you preform CRUD operations against the session. Once you flush the session all the CRUD operations execute (this is the quick and dirty overview).
The recomendation (requirement) is that ISession should never cross threads as it's not thread safe. so in most cases, especially web development, you see a session is created at the start of the thread and flushed at the end of the thread (begin and end request with asp.net).

Sessions are created from ISessionFactory. ISF should be a singleton. This object is usually created when the application is first loaded and disposed when the application exists.

so in the example of a DataExporter. I would have a DataExporterFactory which is the singleton and returns a single instance of DataExporter per thread. DataExporter would be a transient object.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
I see what you're saying. Since the job requisition asks me to do it the way I'm doing it, I'm going to keep it that way. But I like your idea better.

I have another question about adding a custom event to an interface, which I will post on a new thread. Thanks for your help, Jason.
 
no problem, I enjoyed the discussion as well.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top