To avoid the GC problem @Henk's code has, you can use a ConditionalWeakTable
(available on .NET 4.0 an later), which is doesn't prevent the GC from collecting its keys and values. I use long
s for the IDs, since that way we don't need to worry about integer overflows in long running applications.
public class UniqueId<T>
where T:class
{
long counter = 0;
ConditionalWeakTable<T, object> ids = new ConditionalWeakTable<T,object>();
public long GetId(T obj)
{
return (long)ids.GetValue(obj, _ => Interlocked.Increment(ref counter));
}
}
For your problem you can use:
var ids = new UniqueId<Thread>();
ids.GetId(thread1) // will assign it the id 1
ids.GetId(thread2) // will assign it the id 2 (assuming it's a different thread)
ids.GetId(thread1) // will reuse the existing id 1
This code is thread-safe since Interlocked.Increment
generates unique ids and ConditionalWeakTable
is threadsafe as well. But it can skip ids if several threads query the id of a thread that has no id yet. Like the documentation for GetValue
says:
If multiple threads try to create the same key, createValueCallback
may be invoked multiple times with the same key. Only one of these calls will succeed, and its returned value will be added to the table. Which thread succeeds in creating the value is indeterminate.
This should be fine for your application, but if it's unacceptable you can use a lock to prevent multiple threads from generating an ID at the same time.