Parallel.Invoke - Exception handling

坚强是说给别人听的谎言 提交于 2020-01-22 14:52:10

问题


My code runs 4 function to fill in information (Using Invoke) to a class such as:

class Person
{
    int Age;
    string name;
    long ID;
    bool isVegeterian

    public static Person GetPerson(int LocalID)
    {
        Person person;
        Parallel.Invoke(() => {GetAgeFromWebServiceX(person)}, 
                        () => {GetNameFromWebServiceY(person)},
                        () => {GetIDFromWebServiceZ(person)},
                        () => 
                        {
                           // connect to my database and get information if vegeterian (using LocalID)
                           ....
                           if (!person.isVegetrian)
                               return null
                           ....
                        });
     }
}

My question is: I can not return null if he's not a vegeterian, but I want to able to stop all threads, stop processing and just return null. How can it be achieved?


回答1:


To exit the Parallel.Invoke as early as possible you'd have to do three things:

  1. Schedule the action that detects whether you want to exit early as the first action. It's then scheduled sooner (maybe as first, but that's not guaranteed) so you'll know sooner whether you want to exit.
  2. Throw an exception when you detect the error and catch an AggregateException as Jon's answer indicates.
  3. Use cancellation tokens. However, this only makes sense if you have an opportunity to check their IsCancellationRequested property.

Your code would then look as follows:

var cts = new CancellationTokenSource();
try
{
    Parallel.Invoke(
        new ParallelOptions { CancellationToken = cts.Token },
        () =>
        {
            if (!person.IsVegetarian)
            {
                cts.Cancel();
                throw new PersonIsNotVegetarianException();
            }
        },
        () => { GetAgeFromWebServiceX(person, cts.Token) }, 
        () => { GetNameFromWebServiceY(person, cts.Token) },
        () => { GetIDFromWebServiceZ(person, cts.Token) }
    );
}
catch (AggregateException e)
{
    var cause = e.InnerExceptions[0];
    // Check if cause is a PersonIsNotVegetarianException.
}

However, as I said, cancellation tokens only make sense if you can check them. So there should be an opportunity inside GetAgeFromWebServiceX to check the cancellation token and exit early, otherwise, passing tokens to these methods doesn't make sense.




回答2:


Well, you can throw an exception from your action, catch AggregateException in GetPerson (i.e. put a try/catch block around Parallel.Invoke), check for it being the right kind of exception, and return null.

That fulfils everything except stopping all the threads. I think it's unlikely that you'll easily be able to stop already running tasks unless you start getting into cancellation tokens. You could stop further tasks from executing by keeping a boolean value to indicate whether any of the tasks so far has failed, and make each task check that before starting... it's somewhat ugly, but it will work.

I suspect that using "full" tasks instead of Parallel.Invoke would make all of this more elegant though.




回答3:


Surely you need to load your Person from the database first anyway? As it is your code calls the Web services with a null.

If your logic really is sequential, do it sequentially and only do in parallel what makes sense.



来源:https://stackoverflow.com/questions/3724232/parallel-invoke-exception-handling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!