Is Singleton collection of Singletons possible??

D

Daniel Billingsley

Let's say I'm writing a business app and I want there to be only one
instance of the Customer object for each particular customer (representing a
database record) being edited.

Would it be possible to extend the Singleton pattern to handle this?

Assuming that my Customer class follows the Singleton pattern (particularly
Skeet's 4th version) I'm thinking if I add

private static SomeCollectionType customers;

and then change the factory to something like

public static GetCustomer(Guid customerID)
{
// check collection and return existing instance if found
// create new instance and return if not
}

I think I have my mind around the static and singleton concepts, but need to
make sure.
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Let's say I'm writing a business app and I want there to be only one
instance of the Customer object for each particular customer (representing a
database record) being edited.

Would it be possible to extend the Singleton pattern to handle this?

Sort of - it would then be called the Factory pattern though :)
Assuming that my Customer class follows the Singleton pattern (particularly
Skeet's 4th version) I'm thinking if I add

private static SomeCollectionType customers;

and then change the factory to something like

public static GetCustomer(Guid customerID)
{
// check collection and return existing instance if found
// create new instance and return if not
}

I think I have my mind around the static and singleton concepts, but need to
make sure.

That would be fine - but for thread-safety you should basically have a
lock which is acquired *every* time GetCustomer is called. The fourth
singleton pattern isn't really much use to you here. The second pattern
is much closer to what you'd need to use.
 
D

Daniel Billingsley

Yeah but I would need the Factory to be a Singleton also or it defeats the
purpose. So I need a GetFactory() and then a GetCustomer() I guess.

As to the locking... help me sort this out in my head. The problem is two
threads could call GetCustomer() right? Since that isn't addressed by the
Singleton nature of the factory itself the locks are required.

So I could follow the 4th pattern for the factory itself and then use the
locks within GetCustomer(). Right? Not that the locking in the overall
pattern probably matters much at that point since I'm going to be locking
left and right in the other method anyway.
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Yeah but I would need the Factory to be a Singleton also or it defeats the
purpose. So I need a GetFactory() and then a GetCustomer() I guess.

Well, the factory methods themselves needn't be part of an instance -
you don't need to have a separate class. Any reason not just to have a
static GetCustomer method?
As to the locking... help me sort this out in my head. The problem is two
threads could call GetCustomer() right?
Yes.

Since that isn't addressed by the
Singleton nature of the factory itself the locks are required.
Yes.

So I could follow the 4th pattern for the factory itself and then use the
locks within GetCustomer(). Right?

Yes, if you want to.
Not that the locking in the overall
pattern probably matters much at that point since I'm going to be locking
left and right in the other method anyway.

Right.
 
D

Daniel Billingsley

Yes I think I see your point. There's no need for there to ever be an
instance of the factory, as it's only purpose is to expose the static
GetCustomer() (in a Singleton way, of course).

I think I understand that while I could be assured there is only one static
collection, multiple threads calling GetCustomer() could be adding, objects
to it, which would most likely result in a mess. In other words, it's the
fact that my main Singleton object is a collection type, which is by
definition continually changing, that's the problem.

But it appears maybe Microsoft is ahead of me. This is from the Thread
Safety section for HybridDictionary:
"Public static (Shared in Visual Basic) members of this type are safe for
multithreaded operations. Instance members are not guaranteed to be
thread-safe.

This implementation does not provide a synchronized (thread-safe) wrapper
for a HybridDictionary, but derived classes can create their own
synchronized versions of the HybridDictionary using the SyncRoot property.

Enumerating through a collection is intrinsically not a thread-safe
procedure. Even when a collection is synchronized, other threads could still
modify the collection, which causes the enumerator to throw an exception. To
guarantee thread safety during enumeration, you can either lock the
collection during the entire enumeration or catch the exceptions resulting
from changes made by other threads."

So if my singleton factory inherited from this HybridDictionary and followed
the fourth pattern, am I good, since 1) the factory itself would be a
singleton and 2) the public static collection would is thread-safe? (The
above caveat about enumeration taken into account, of course)

Or are they just locking internally anyway when they say it is thread-safe?
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Yes I think I see your point. There's no need for there to ever be an
instance of the factory, as it's only purpose is to expose the static
GetCustomer() (in a Singleton way, of course).

Well, in a thread-safe way, effectively.
I think I understand that while I could be assured there is only one static
collection, multiple threads calling GetCustomer() could be adding, objects
to it, which would most likely result in a mess. In other words, it's the
fact that my main Singleton object is a collection type, which is by
definition continually changing, that's the problem.

But it appears maybe Microsoft is ahead of me. This is from the Thread
Safety section for HybridDictionary:
"Public static (Shared in Visual Basic) members of this type are safe for
multithreaded operations. Instance members are not guaranteed to be
thread-safe.

That's not particularly unique to HybridDictionary, of course. Most
classes in .NET are documented in that way...
So if my singleton factory inherited from this HybridDictionary and followed
the fourth pattern, am I good, since 1) the factory itself would be a
singleton and 2) the public static collection would is thread-safe? (The
above caveat about enumeration taken into account, of course)

Or are they just locking internally anyway when they say it is thread-safe?

I'm afraid I've lost the plot slightly at this point - but I'm sure
you'll have to do *some* locking yourself to get the behaviour you
want. A simple implementation of:

static Hashtable customers = new Hashtable();

static object padlock = new object();

static Customer GetCustomer (string id)
{
lock (padlock)
{
if (!customers.ContainsKey(id))
{
customers[id] = new Customer(id);
}
return (Customer) customers[id];
}
}

or something similar would be your best bet, I believe.
 
D

Daniel Billingsley

Sorry I lost you - it's my confusion rubbing off probably. :) I realize
that thread-safe claim would be true of most if not all of the collection
classes.

I don't think your example below assures there is only one collection, does
it? Or did you just intentionally leave out those details?

My thinking is that since MS says the collections are thread-safe I wouldn't
need to worry about the locking if GetCustomer(). In the case of the
Hashtable there's a synchronized wrapper, so this is what I'm thinking: (Of
course, if all the syncronized version is doing is using locks anyway than
it probably doesn't matter.)

public class CustomerCollection
{

static CustomerCollection customers = new CustomerCollection();
static CustomerCollection syncedCustomers =
Hashtable.Synchronized(customers);

static CustomerCollection()
{
}

private CustomerCollection()
{
}

public static Customer GetCustomer(string id)
{
if (!syncedCustomers.ContainsKey(id))
{
syncedCustomers[id] = new Customer(id);
}
return (Customer) syncedCustomers[id];
}

}
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Sorry I lost you - it's my confusion rubbing off probably. :) I realize
that thread-safe claim would be true of most if not all of the collection
classes.

I don't think your example below assures there is only one collection, does
it? Or did you just intentionally leave out those details?

It ensures there's only one collection per AppDomain, certainly -
because it's only created when the static initializer runs.
My thinking is that since MS says the collections are thread-safe I wouldn't
need to worry about the locking if GetCustomer().

No, because you need to go through the process of:

1) Test if the customer is already there
2) If it is, return it
3) If not, create a new customer and put it in the collection

Another thread could come in and request the same ID between steps 1
and 3, meaning you end up with two customers for the same ID. Only each
individual step is thread-safe without locking - you need to
effectively do the whole operation atomically.
 
D

Daniel Billingsley

Well, of course. But for thread-safeness it's the 3rd step that is of
significance. That is to say, setting aside my specific application, the
problem is two threads attempting to add the same ID. I take "A Hashtable
can support one writer and multiple readers concurrently. To support
multiple writers, all operations must be done through this wrapper only." to
mean the synchronized Hashtable itself wouldn't allow that, and I'd end up
with an ArgumentException. How do you take it? (That's assuming I use the
Add method which maybe is not the code you posted before as I recall.)

Now, that's not to say that using locking isn't better than dealing with the
exceptions, if that's where you'll go next. I'll have to evaluate that. I
just want to make sure I'm understanding these concepts correctly first
though.
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Well, of course. But for thread-safeness it's the 3rd step that is of
significance. That is to say, setting aside my specific application, the
problem is two threads attempting to add the same ID. I take "A Hashtable
can support one writer and multiple readers concurrently. To support
multiple writers, all operations must be done through this wrapper only." to
mean the synchronized Hashtable itself wouldn't allow that, and I'd end up
with an ArgumentException. How do you take it? (That's assuming I use the
Add method which maybe is not the code you posted before as I recall.)

No, you wouldn't end up with an ArgumentException - you'd end up with
each call locking all other calls out until it completed.
Now, that's not to say that using locking isn't better than dealing with the
exceptions, if that's where you'll go next. I'll have to evaluate that. I
just want to make sure I'm understanding these concepts correctly first
though.

The problem is that if two different threads can both reach step 2, you
create more than one object for the same ID, which is usually a bad
move.

With threading, I always take the "simple is best" approach: while
you're dealing with shared data, take out a lock. I think people
generally believe that locks are more expensive than they really are.
 
D

Daniel Billingsley

Jon Skeet said:
No, you wouldn't end up with an ArgumentException - you'd end up with
each call locking all other calls out until it completed.
The problem is that if two different threads can both reach step 2, you
create more than one object for the same ID, which is usually a bad
move.

Doesn't that contradict the doc, which explicitly says it supports multiple
writers in a thread-safe way? For the Add method it says the
ArgumentExeption means "An element with the same key already exists in the
Hashtable".

Up through Steps 3a, nothing has yet been created in the Hashtable. At that
point the two threads only "think" they're going to add the same key.
According to the information in the doc, one of them will ultimately fail in
step 3b.
With threading, I always take the "simple is best" approach: while
you're dealing with shared data, take out a lock. I think people
generally believe that locks are more expensive than they really are.

I agree, which is why I will evaluate whether just adding the locking would
be simpler than dealing with the exceptions. I expect it will. At this
point I'm trying to figure out why we're understanding Microsoft's
explanation of this so differently. Maybe at this point we're just using
different symantecs.

The doc also says "Even when a collection is synchronized, other threads
could still modify the collection, which causes the enumerator to throw an
exception. To guarantee thread safety during enumeration, you can either
lock the collection during the entire enumeration or catch the exceptions
resulting from changes made by other threads."

Although it doesn't say so, I would expect the same problem to exist with
Contains() and ContainsKey() which I would expect are just enumerating
through under the hood. That being the case, I've got to handle several
possible exceptions and I think just locking myself is indeed way simpler.

Here's the steps again, for reference
1) Test if the customer is already there
2) If it is, return it
3) If not, (a) create a new customer and (b) put it in the collection

 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Doesn't that contradict the doc, which explicitly says it supports multiple
writers in a thread-safe way?

Not at all.
For the Add method it says the
ArgumentExeption means "An element with the same key already exists in the
Hashtable".

Assuming you use Add rather than the indexer, that just means you'll
get an ArgumentException - which isn't a good thing, is it?
Up through Steps 3a, nothing has yet been created in the Hashtable. At that
point the two threads only "think" they're going to add the same key.
According to the information in the doc, one of them will ultimately fail in
step 3b.

Yes, but that's hardly a good way of working, IMO.
I agree, which is why I will evaluate whether just adding the locking would
be simpler than dealing with the exceptions. I expect it will. At this
point I'm trying to figure out why we're understanding Microsoft's
explanation of this so differently. Maybe at this point we're just using
different symantecs.

Locking would be simpler, more elegant, and almost certainly about as
performant.
The doc also says "Even when a collection is synchronized, other threads
could still modify the collection, which causes the enumerator to throw an
exception. To guarantee thread safety during enumeration, you can either
lock the collection during the entire enumeration or catch the exceptions
resulting from changes made by other threads."

Although it doesn't say so, I would expect the same problem to exist with
Contains() and ContainsKey() which I would expect are just enumerating
through under the hood.

No, because they can take out the internal synchronization lock for the
whole of their operation.
 
D

Daniel Billingsley

Jon Skeet said:
No, you wouldn't end up with an ArgumentException - you'd end up with
each call locking all other calls out until it completed.

Well, yes maybe so, but then the 2nd thread would get an ArgumentException
when it tried to use an existing key in the Add method, as is normally the
case in that method, wouldn't it? How else could you justify the statements
a) multiple writers are supported through the synchronized wrapper and b)
the ArgumentException is raised with Add is used with an existing key?

Again, handling the exceptions would likely be more trouble than its worth
as compared to just doing the locking myself, but it seems it would be
possible. I'm trying to understand why you think otherwise - i.e. what I'm
missing.
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Well, yes maybe so, but then the 2nd thread would get an ArgumentException
when it tried to use an existing key in the Add method, as is normally the
case in that method, wouldn't it? How else could you justify the statements
a) multiple writers are supported through the synchronized wrapper and b)
the ArgumentException is raised with Add is used with an existing key?

That a writer can use methods other than Add. In particular, I usually
use the indexer, which *doesn't* throw an ArgumentException. Otherwise
yes, with Add you would get an exception the second time, if you're
using the same key. (Using the unsynchronized version, you wouldn't
even be guaranteed to keep the data consistent though - both calls may
succeed, with one of the adds being lost, whether or not they're using
the same key. I suspect it wouldn't happen, but it's not guaranteed
that it wouldn't.)
Again, handling the exceptions would likely be more trouble than its worth
as compared to just doing the locking myself, but it seems it would be
possible. I'm trying to understand why you think otherwise - i.e. what I'm
missing.

Possibly only the option of not using an indexer.
 
D

Daniel Billingsley

Ok, I think we're getting on the same page. I was talking specifically
about using Add(). This threading stuff is fairly new ground to me and I'm
trying to make sure I understand properly.

Jon Skeet said:
Daniel Billingsley <[email protected]> wrote:
That a writer can use methods other than Add. In particular, I usually
use the indexer, which *doesn't* throw an ArgumentException. Otherwise
yes, with Add you would get an exception the second time, if you're
using the same key. (Using the unsynchronized version, you wouldn't
even be guaranteed to keep the data consistent though - both calls may
succeed, with one of the adds being lost, whether or not they're using
the same key. I suspect it wouldn't happen, but it's not guaranteed
that it wouldn't.)

Is there some source in the docs that leads you to that conclusion, or just
your deep understanding of how that wrapper works? The doc in the Hashtable
section seems to just state plainly that the synchronized wrapper supports
multiple writers. Are you saying that it may not in fact do so properly?
If so, then in general what are we to make of it when we see statements from
Microsoft that certain classes or methods are thread-safe? Couldn't the
wrapper simply be doing the same kind of lockout you're talking about?
 
J

Jon Skeet [C# MVP]

Daniel Billingsley said:
Ok, I think we're getting on the same page. I was talking specifically
about using Add(). This threading stuff is fairly new ground to me and I'm
trying to make sure I understand properly.
Right.




Is there some source in the docs that leads you to that conclusion, or just
your deep understanding of how that wrapper works? The doc in the Hashtable
section seems to just state plainly that the synchronized wrapper supports
multiple writers. Are you saying that it may not in fact do so properly?

No, it does so - I was saying that using the *unsynchronized* version
it doesn't support multiple writers.
If so, then in general what are we to make of it when we see statements from
Microsoft that certain classes or methods are thread-safe? Couldn't the
wrapper simply be doing the same kind of lockout you're talking about?

The wrapper does per-method-call locking. You want to lock a whole
sequence of operations, namely "check hashtable/create object/add
entry".
 
D

Daniel Billingsley

Doh! I guess if I could read it would help matters a great deal. Totally
missed that "un". ;)

Anyway, it seems fairly obvious to me at this point that locking the whole
deal is much simpler and likely better performing since there's only one
lock and I really don't expect a lot of thread contention, if any.

Thanks once again for your patience, Jon.
 
G

Guest

Hi Daniel,

Do you still have concern?

If you need further help, please feel free to feedback, we will help you.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top