Question

I have the following context:

private void btn_Click(object sender, EventArgs e)
{
    Thread newThread = new Thread(Step1);
    newThread.Start(this);
}

private void Step1(object stateInfo)
{
    Register();

    // work

    Thread newThread = new Thread(Step2);
    newThread.Start(this);
}

private void Step2(object stateInfo)
{
    Register();

    // work

    Thread newThread = new Thread(Step3);
    newThread.Start(this);
}

private void Step3(object stateInfo)
{
    Register();

    // work
    // finish
}

private void Register()
{
    // adds a new entry to a Dictionary,
    // where the key is the Thread's ID (System.Threading.Thread.CurrentThread.ManagedThreadId)
    // and the value is a resource 
    // the goal is for a Thread not to register itself twice, or not to change its resource 
}

As the comments say, I need for each Thread to save a resource. This should be done only once per Thread and shouldn't be modifiable.

The problem is that, sometimes, the Thread from Step3 has the same ID as the Thread from Step1. I think it makes sense; because by the time it gets to Step3, Step1's Thread is already 'dead' and its ID can be reused.

Do you have any suggestions? Is there something wrong with my approach? Is there a way of obtaining/generating an unique ID for all the Threads that a Process creates?

[Edit 1] Included the .NET 2.0 constraint.

Était-ce utile?

La solution

Threads are objects so you can simply rely on the built-in equality system:

private Dictionary<Thread, R> store = new Dictionary<Thread, R> ();
private void Register()
{
    var r = ..
    store.Add(Thread.CurrentThread, r);
}

reference equality will ensure that you won't have duplicates. You should be aware though that your dictionary is blocking the Threads from being GCed. You will need to clean up once in a while.

And if you don't want to keep the Threads around, then create your own simple id generator with Interlocked.Increment()

Autres conseils

You should probably use a ThreadLocal<T> object or a ThreadStatic static field, these will have their content erased when the thread dies, and won't be reused even if a new thread with the same managed thread ID spawns.

See here: ThreadLocal and ThreadStatic

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 longs 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top