开发者

How do I control the access of multiple threads to a collection of objects?

I'm writing an application that displays a list of objects which the user can select and then view and edit the properties of through a PropertyGrid control. The object's properties are populated by time consuming process of extracting information from files through the use of a secondary thread. But I'd also like to allow the user to continue viewing other objects as the extraction process is proceeding.

After reading the responses to my previous questions on SO. It sounds like because the properties that开发者_开发问答 are being written by the extraction process do not intersect the properties that are editable by the user through the property grid I shouldn't have an issue with the two threads editing the objects at the same time. Although it's possible for the user to see an incorrect value if they are incredibly unlucky and the property grid end up reading the object in the middle of a non-atomic write.

However, I'd still like to know how I might set this up to prevent the user from editing or viewing an object which is in the middle of being extracted. I'm very very new to multithreading, but most examples that I've read so for show a separate token Object being created to use to lock access to to the actual Object of interest. The answers to my other previous question confirmed that it is typical to create a separate object like this specifically for locking.

So now what I'd like to know is how is this handled in my case where I have a large collection of objects? I'd like to create locks that prevents the property grid from displaying the object a user selects if it is currently being extracted to.

Do I need to create a separate collection of lock Objects that is kept in sync with my real collection? So if an object is added or removed from my main collection I have to add or remove lock objects from my lock collection?

Do I lock to the actual objects rather then creating separate token lock objects?

What about adding a "IsBeingExtracted" Boolean property to the objects that the property grid can check to see if the object is in the middle of being written to? This would then be set at the very beginning and very end of the extraction process.

Or how about a static field somewhere that references the current (if any) object that is current being extracted to. The property grid could then check that the latest object it was ask to display was not the object referenced by this static field? This of course wouldn't work if there were multiple extraction threads.

What's the best/correct way to do this? Personally I like the boolean property option the best, but would like to know what others who actually know what they are doing think.


Couldn't you just make the collection that holds the objects a SynchronizedCollection<T>? It will make sure that only one thread can add or access objects at a time. Then, rather than worry about how to synchronize access to each object's properties, don't add the object to the collection until it has been populated.

Something like this:

private readonly ICollection<Item> Items = new SynchronizedCollection<Item>();

// Run this on the background thread.
public void PopulateItems()
{
    using (var file = File.OpenRead("BigFile.txt"))
    using (var reader = new StreamReader(file))
    {
        while (!reader.EndOfStream)
        {
            var item = new Item();
            PopulateItem(item);
            Items.Add(item);
        }
    }
}

public void PopulateItem(Item item)
{
    // Do time-consuming work.
}

The property grid can happily do whatever it wants with the object because by the time it shows up in the list, the extraction thread is finished with it. No explicit locking required.


Make your collection of objects a Dictionary and then lock on the keys.


In general, using one or several locks depends on the degree of concurrency that you want to achieve.

The less locks you have (say, one global lock), the less concurrency there'll be because many operations can compete for the lock and prevent each other from running.

The more locks you have, the more concurrency you can achieve, but the more overhead there is in managing locks.

Using a lock that is the object being modified makes sense if what you are protecting with the lock is only this object. You'll see many examples using a separate object because you then don't need to thing about the scope of the protection.

In your specific case, I do not really understand what is being protected. Are you trying to prevent the concurrent modification of a property of an object, i.e. by the background process or the user ? As a user cannot do more than one thing at a time, there is no need for lots of concurrency, thus there is no need to have many locks. What you could do is have a single lock that is taken when the property grid enters editing mode and when a background process is about to set the same property being edited (otherwise, there is no need for locks).


I think the best way would be to use the ManualResetEvent object with a timeout. That way you can tell the user that it is being extracted and to try again in a few seconds.

public class Item : IDisposable // I know it is a horrible class name...
{
    private readonly ManualResetEvent accessibleEvent = new ManualResetEvent(false);

    public void Extract()
    {
        try
        {
            // .....
        }
        finally
        {
            accessibleEvent.Set(); // unlock             
        }
    }

    public void Edit()
    {
        if (!accessibleEvent.WaitOne(1000)) // wait 1 second
        {
            // notify user?    
        }

        // ....
    }

    public void Dispose()
    {
        ((IDisposable)accessibleEvent).Dispose();
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜