Reputation: 2160
I have a foreach loop with Linq queries inside.
Everything runs great until I change the foreach to Parallel.ForEach:
// get the task info ---------
Log("Populate task, guf code lists ...........................");
List<SF_CO_ITEM> tasks = (from coi in ctx.SF_CO_ITEM
where coi.CO == co.ID
select coi).ToList();
// foreach (SF_CO_ITEM t in tasks)
// {
Parallel.ForEach(tasks, t =>
{
Log("Executing on t: " + t.ID);
// exception on next line:
List<SF_CO_LINE_ITEM> gufs = (from coli in ctx.SF_CO_LINE_ITEM
where coli.CO_ITEM == t.ID
select coli).ToList();
And the exception I get is:
System.AccessViolationException was unhandled Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt. Source=Oracle.DataAccess StackTrace: at Oracle.DataAccess.Client.OpsCon.Open(IntPtr& opsConCtx, IntPtr& opsErrCtx, OpoConValCtx* pOpoConValCtx, OpoConRefCtx& pOpoConRefCtx) at Oracle.DataAccess.Client.ConnectionDispenser.Open(OpoConCtx opoConCtx) at Oracle.DataAccess.Client.OracleConnection.Open() at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure) at System.Data.EntityClient.EntityConnection.Open() at System.Data.Objects.ObjectContext.EnsureConnection() at System.Data.Objects.ObjectQuery
1.GetResults(Nullable
1 forMergeOption) at System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Collections.Generic.List
1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source) at ChangeOrder.Program.<>c_DisplayClass19.b_16(SF_CHANGE_ORDER_ITEM t) in C:\VS_apps\PMConsole\PMC Tools\ChangeOrderExecution\Program.cs:line 220 at System.Threading.Tasks.Parallel.<>c_DisplayClass2d2.<ForEachWorker>b__23(Int32 i) at System.Threading.Tasks.Parallel.<>c__DisplayClassf
1.b_c() at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask) at System.Threading.Tasks.Task.<>c_DisplayClass7.b_6(Object ) at System.Threading.Tasks.Task.ExecuteSelfReplicating(Task root) at System.Threading.Tasks.Task.Execute() at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot) at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution) at System.Threading.Tasks.ThreadPoolTaskScheduler.TryExecuteTaskInline(Task task, Boolean taskWasPreviouslyQueued) at System.Threading.Tasks.TaskScheduler.TryRunInline(Task task, Boolean taskWasPreviouslyQueued, Object threadStatics) at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler) at System.Threading.Tasks.Task.RunSynchronously(TaskScheduler scheduler) at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action1 body, Action
2 bodyWithState, Func4 bodyWithLocal, Func
1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IList
1 list, ParallelOptions parallelOptions, Action1 body, Action
2 bodyWithState, Action3 bodyWithStateAndIndex, Func
4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func
1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable
1 source, ParallelOptions parallelOptions, Action1 body, Action
2 bodyWithState, Action3 bodyWithStateAndIndex, Func
4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func
1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable
1 source, Action`1 body) at ChangeOrder.Program.PerformChangeOrder(SF_CHANGE_ORDER co, SF_CLIENT_PROJECT cp, SFEntitiesQA ctx) in C:\VS_apps\PMConsole\PMC Tools\ChangeOrderExecution\Program.cs:line 216 at ChangeOrder.Program.Main(String[] args) in C:\VS_apps\PMConsole\PMC Tools\ChangeOrderExecution\Program.cs:line 1373 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() InnerException:
I'm not sure what I would need to lock since I am just grabbing information (just RO, right?).
I've thought about adding the "AsParallel" but my understanding is that's a PLINQ directive that just causes the query to run in parallel with respect to itself.
I can't find any examples of people running Linq queries inside Parallel.ForEach loops, so I'm not even sure what I'm doing is allowed.
Upvotes: 0
Views: 2481
Reputation: 3892
Why not just do the join itself instead of getting each one individually? Since it looks like this is hitting a DB, your LINQ provider should just compose the query and get you the rows you're looking for. Try this:
List<SF_CO_LINE_ITEM> gufs;
var query = from coi in ctx.SF_CO_ITEM
where coi.CO == co.ID
join coli in ctx.SF_CO_LINE_ITEM
on coi.ID == coli.CO_ITEM
select coli;
// Confirm what the query looks like by calling 'query.ToString()'
gufs = query.ToList();
I'll usually separate the query from the actual enumeration/materialization, that way I can validate that the query looks like I want. If this is a 1:M relationship between SF_CO_ITEM
and SF_CO_LINE_ITEM
, then you should do a GroupJoin by changing the join to:
join coli in ctx.SF_CO_LINE_ITEM
on coi.ID == coli.CO_ITEM into tcoli
from tc in tcoli
select tc
As for why you're getting this exception, might have something to do with trying to access the context from a different thread. As per MSDN article on Parallel loops:
Hidden Loop Body Dependencies
Incorrect analysis of loop dependencies is a frequent source of software defects. Be careful that all parallel loop bodies do not contain hidden dependencies. This is a mistake that's easy to make.
The case of trying to share an instance of a class such as Random or DbConnection, which are not thread safe, across parallel iterations is an example of a subtle dependency.
So your only options are to keep it sequential instead of parallel or to alter the original query to a join so that you get the right data the first time around.
Hope that helps!
Upvotes: 2