Here's some code just to hopefully make clear the situation I'm talking about:
public class Processor
{
private readonly IRepository _repo;
private readonly IApiSrevice _apiService
private readonly _mapper;
public Processor(IRepository repo, IApiSrevice apiService, IMapper mapper)
{
_repo = repo;
_apiService = apiService
_mapper = mapper;
}
public async Task<IEnumerable<Thing>> ProcessStuff(IEnumerable<MyDto> dtos)
{
var people = await _apiService.GetPeople();
ConcurrentBag<Location> things = new();
var options = new ParallelOptions { MaxDegreeOfParallelism = 3 };
await Parallel.ForEachAsync(people, options, async(person, token ) =>
{
var locations = await _apiService.GetLocations(person.Id);
IEnumerable<Thing> newThings = _mapper.Map(locations);
// maybe there's a repo call in here somewhere
// _repo.AddThings(newThings);
foreach(var thing in newThings)
{
things.Add(thing)
}
});
return things;
}
}
I think that just because of the nature of interfaces (hidden implementations) calling any method on one from within a Parallel loop is a bad idea: implementations might have methods that aren't thread-safe.
If so, how can I call out to methods on interfaces? I've done quite a bit of testing, both with Parallel.ForEachAsync() and a standard foreach loop, and I get identical results, but I'm not sure this is something I can count on. Running with the Parallel loop and 6 degrees of parallelism takes significantly less time, though.
SELECT * FROM Person where UnindexedID=@idwill cause a full table scan 6 times, taking shared locks on the entire table, blocking modifications and probably causing deadlocks.SELECT ... WHERE unindexedid in (@id1, @id2,...,@id6)will only do so once. If theIDfield is indexed,INis still better because the overhead of 6 connections is probably higher than the cost of the query itself.