Automapper/StructureMap issues with TPL version of

2019-08-09 13:08发布

问题:

This is my synchronous working version for some workers to do some different work given a generic workload:

foreach (var worker in _workers)
{
    worker.DoWork(workload);
}

I am trying to exploit existing cores via the task parallel library (TPL) using this:

foreach (var worker in _workers)
{
    var worker1 = worker;
    await Task.Run(() => worker1.DoWork(workload));
}
await Task.WhenAll();

The intention is, that each worker is executed in its own thread. Please note that this runs in a async method, which receives a 'workload' from time to time. I want to ensure that all work is done before the foreach is run again. Hence, the line:

await Task.WhenAll();  

Unfortunately I seem to have some sporadic mapping exception related to automapper/structuremap (it works fine synchronously). This is my structuremap code:

public class MyRegistry : Registry
{
    public MyRegistry()
    {
        For<ISomething>().Singleton().Use<SomethingConcrete>();

        var profiles =
        from t in typeof(MyRegistry).Assembly.GetTypes()
        where typeof(Profile).IsAssignableFrom(t)
        select (Profile)Activator.CreateInstance(t);

        var config = new MapperConfiguration(cfg =>
        {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
        });

        For<MapperConfiguration>().Use(config);
        For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
    }
}

To isolate the problem, is there something fundamentally wrong with the TPL code to start with?

回答1:

Is there something fundamentally wrong with the TPL code?

Yes. await Task.WhenAll() doesn't do anything as it's supposed to accept tasks as parameters. And offloading to the ThreadPool with Task.Run but awaiting each task sequentially doesn't either.

What you probably wanted to do is this:

var tasks = new List<Task>();
foreach (var worker in _workers)
{
    tasks.Add(Task.Run(() => worker.DoWork(workload)));
}

await Task.WhenAll(tasks);

Or more simply this:

await Task.WhenAll(_workers.Select(worker => Task.Run(() => worker .DoWork(workload)));

Which creates a task for each worker that will run on the ThreadPool and then uses Task.WhenAll to await the completion of all these tasks.