Reputation: 48187
I'm using this example to create partitions and is working perfectly in my VS2015 app. I tried to move the code to VS2013, using 4.5.2 framework, and now have an error on this line of code:
VS2013 12.0.40629.00 Update 5
body: (source, state, local) =>
says
Error 5 Delegate 'System.Func<System.Tuple<long,long>,System.Threading.Tasks.ParallelLoopState,long,AnonymousType#1,AnonymousType#1>' does not take 3 arguments
I found this question: Whats wrong in this Parallel.For Code? which is very similar. But it does not appear to actually be the same problem.
The suggested answer doesn't have any syntax error in 2013 but doesn't use partitions, so I'm not sure how adapt my code to that one. And the recommendation is to add three parameters and mine already has three parameters.
This is a reduced version of my code:
public void NearLinkParallelGeneration(avl_range avl_pending)
{
var parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount + 2
};
var partitions = Partitioner
.Create(
fromInclusive: avl_pending.begin,
toExclusive: avl_pending.end,
rangeSize: 100
)
.GetDynamicPartitions();
Parallel.ForEach(
source: partitions,
parallelOptions: parallelOptions,
localInit: () =>
{
NpgsqlConnection conn = new NpgsqlConnection(strConnection);
NpgsqlCommand cmd = new NpgsqlCommand();
conn.Open();
return new { Connection = conn, Command = cmd };
},
body: (source, state, local) => -- HERE IS THE ERROR
{
return local;
},
localFinally: local =>
{
local.Connection?.Dispose();
local.Command?.Dispose();
}
);
Upvotes: 1
Views: 245
Reputation: 70652
I don't have VS2013 installed, so I'm unable to reproduce your problem. However, it appears that the compiler is getting confused about which overload to select.
The code you are copying uses the Parallel.ForEach<TSource, TLocal> Method (IEnumerable<TSource>, ParallelOptions, Func<TLocal>, Func<TSource, ParallelLoopState, TLocal, TLocal>, Action<TLocal>)
overload of the ForEach()
method.
The error message you're getting makes it clear that the compiler has selected the Parallel.ForEach<TSource, TLocal> Method (IEnumerable<TSource>, ParallelOptions, Func<TLocal>, Func<TSource, ParallelLoopState, Int64, TLocal, TLocal>, Action<TLocal>)
overload for your call. That is, it's expected a delegate that will receive not just the source
, state
, and local
values, but also the index
for the current item.
Lacking the ability at the moment to test the code in VS2013, I'm not entirely sure why the compiler has selected the wrong overload. But I think it's possible it's related to the issue described at 'Delegate 'System.Action' does not take 0 arguments.' Is this a C# compiler bug (lambdas + two projects)?. With the answer being that, yes, it's a compiler bug, as result of deferred caching gone wrong of overload metadata on the part of the compiler.
If this is in fact the case, you should be able to work around the issue by providing explicit types for the lambda parameters. Unfortunately, in your case you are using an anonymous type for the TLocal
type parameter. So you don't have that option, unless you are willing to declare a named type to use instead of the anonymous type.
Another option would be to give up and go along with the compiler's idea of what overload you're calling. Again, I can't test this myself, but I would expect this would work:
Parallel.ForEach(
source: partitions,
parallelOptions: parallelOptions,
localInit: () =>
{
NpgsqlConnection conn = new NpgsqlConnection(strConnection);
NpgsqlCommand cmd = new NpgsqlCommand();
conn.Open();
return new { Connection = conn, Command = cmd };
},
body: (source, state, index, local) // just add the 'index' parameter
{
return local;
},
localFinally: local =>
{
local.Connection?.Dispose();
local.Command?.Dispose();
}
);
Upvotes: 1