Is it OK to lock on System.Collections.Generic.List<t>?
I have been reading about the syncroot element but I can't find it in the List type. So how开发者_运维百科 should the multithreading synchronization be done with the System.Collections.Generic.List<> type?
The reason you can't find it is because it was explicitly removed. If it is really what you want to do, use a SynchronizedCollection<T>
or create a dedicated synchronization object. The best approach (in general) is to create a dedicated synchronization object, as Winston illustrates.
The essential problem with the SyncRoot
property is that it provides a false sense of security -- it only handles a very narrow set of circumstances. Developers often neglect synchronization for an entire logical operation, assuming that locking on SyncRoot
is good enough.
You generally want to avoid locking on a type (List<T>
in this case). If, for example, you have two instances of your type, or another type were to also use a lock on List<T>
, they would all competing for a single global lock. Really, what you are trying to achieve is proper synchronization for a single object.
Why do you want to lock on List<T>
as opposed to your specific instance of a list?
It is often suggested that the best method of locking is to lock on a private object created solely for that purpose.
private readonly object myListLock = new object();
// Everywhere you access myList
lock(myListLock)
{
// do stuff with myList
}
For a great guide to threading in C#, see this Free E-Book (Threading in C#) by Joe Albahari.
You have to cast the generic list to an ICollection
like this:
using System.Collection; // required for ICollection
using System.Collection.Generic;
List<int> myIntList = new List<int>();
lock (((ICollection)myIntList).SyncRoot)
{
// do your synchronized stuff here...
}
Keep in mind that this only synchronizes access to the elements of the generic list. It does not synchronize access to the generic list variable, e.g., myIntList
. If you replace myIntList
with a new list at some point, using SyncRoot
will not be sufficient. In that case, I would recommend creating a specific synchronization object that can be used for both synchronization scenarios.
The answer is Yes, you can use an instance of the list as a synchronizing object:
private readonly List<string> list = new List<string>();
lock(list)
{
// ...
}
So you don't have to use SyncRoot property. Moreover, documentation states that
For collections whose underlying store is not publicly available, the expected implementation is to return the current instance.
i.e. in some cases SyncRoot property returns the collection object itself.
Also read this answer about SyncRoot.
FYI: I've never seen SyncRoot usages in production code. So I suggest you to use instance of any collection as a synchronizing object instead of it's SyncRoot property (as long as the collection is private).
精彩评论