Shared IEnumerable<T> and IQueryable<T> in a multi-threaded application
I've few doubts regarding how shared IEnumerable
and IQueryable
is accessed in multi-threaded application.
Consider this code snippet.
ObservableCollection<SessionFile> files = /* some code */
IEnumerable<Pattern> allFilePatterns= /*some query */
foreach (Pattern pattern in allFilePatterns)
{
string iclFilePath = Path.Combine(pattern.Location, pattern.Filename);
SessionFile sfile = new SessionFile(iclFilePath, pattern.Analys开发者_如何转开发isDate);
SomeDelegate invoker = new SomeDelegate(sfile.SomeHandler);
invoker.BeginInvoke(allFilePatterns, null, null);
files.Add(sfile );
}
As you can see, I'm using BeginInvoke()
passing the same instance allFilePatterns
to each handler called sfile.SomeHandler
.
Suppose in SomeHandler
, I iterate allFilePatterns in a foreach
loop, something like this:
void SomeHandler(IEnumerable<Pattern> allFilePatterns)
{
foreach(Pattern pattern in allFilePatterns)
{
//some code
}
}
Now my doubt is that: since BeginInvoke()
is asynchronous, that means all foreach
in all SomeHandler
of all the files would execute parallelly (each in its own thread), would the shared instance of IEnumerable
enumerate as expected/normal? Is this a right approach? Can I share same instance of IEnumerable
in multiple threads, and enumerate it parallelly?
And what if I use IQueryable
instead of IEnumerable
in the above code? Any side-effect that I should be aware of?
If its not thread-safe, then what should I use?
Please note that I'm using IQueryable
for database queries, as I don't want to pull all the data from database. Therefore, I want to avoid IQueryable.ToList()
as much as possible.
It depends on the implementation. Some implementations of IEnumerable<T>
also implement IEnumerator<T>
, and return themselves from GetEnumerator()
. In that case it's obviously not thread-safe...
As for IQueryable<T>
, it also depends on the implementation. For instance, Entity Framework contexts are not thread-safe, and will only work properly on the thread that created them.
So there is no unique answer to that question... it will probably work for some implementations, and not for others.
I would ToList()
your enumerable when passing as an argument to the delegate to actually create a new set for the thread to work with and avoid problems.
However, I'm wondering why you would need to have each element of the enumerable enumerated N times (effectively N^2)? That sounds inefficient.
EDIT: Updated with my intent
Look at Parallel.For and friends
http://msdn.microsoft.com/en-us/library/dd460693.aspx
Well, one thing you could do, if working with an IQueryable that is not thread-safe (like an Entity Framework query) is to enumerate the results in one thread, but then pass the results to new threads as necessary.
Then it doesn't matter if the IQueryable/IEnumerable is thread-safe.
精彩评论