We help IT Professionals succeed at work.

C# Trap first exception

whorsfall
whorsfall asked
on
Hi,

How can I change the following code to trap an exception the first time it is generated. It seems my code allows multiple tasks to be created before anything responds to the exception.

Thanks,

Ward,



        public async Task<Boolean> New_Scan()
        {
            if (this.Scan_Type == Email_Scan_Type.Test_Data)
            {
                Create_Test_Data();
            }

            if (this.Scan_Type == Email_Scan_Type.Normal || this.Scan_Type == Email_Scan_Type.Normal_Limited)
            {
                await Gather_Email_Items();

                total_item_count = mail_queue.Count;

                var tasks = new List<System.Threading.Tasks.Task>();

                for (int i = 1; i <= this.Number_of_tasks; i++)
                {
                    var t = System.Threading.Tasks.Task.Run(async () =>
                    {
                        await Process_Messages();
                    });

                    tasks.Add(t);

                }

                //System.Threading.Tasks.Task task1 = System.Threading.Tasks.Task.Run(() => Process_Messages());

                try
                {
                    await System.Threading.Tasks.Task.WhenAll(tasks);
                    Add_to_List();
                }

                catch (Exception ex)
                {
                    throw ex;
                }

            }

            return true;
        }

Open in new window

Comment
Watch Question

AndyAinscowFreelance programmer / Consultant

Commented:

Not certain what you mean with 'first'.

Put the try...catch into the Process_Messages function ?

ste5anSenior Developer

Commented:
How can I change the following code to trap an exception the first time it is generated. It seems my code allows multiple tasks to be created before anything responds to the exception.
Well, that's how async concepts work.

Thus the question: What is the problem, when one exceptions happens in one task?

Author

Commented:
Hi AndyAinscow and ste5an,

Thanks for your replies. My fault probably a badly framed question (and not understanding).

Here is the type flow I am after

Lets say I have 4 tasks created in "New_Scan"

Task 1 starts and is running.
Task 2 starts and runs.
Task 3 starts generates and exception.
Task 4 starts generates and exception.
Task 5 starts and runs.

I can trap the exception within New_Scan the exception from Task 3 and Task 4.

I have included my code for Process_Messages to give it a bit more context.

This is what I am hoping for - however please feel free to correct my understanding
if there is a better way or looking at or aproarching the problem.

Thanks again,
Ward.

private async Task<Boolean> Process_Messages()
        {
            EmailMessage myItem;

            PropertySet nps = new PropertySet(
                                    EmailMessageSchema.Body,
                                    EmailMessageSchema.TextBody,
                                    EmailMessageSchema.From,
                                    EmailMessageSchema.Subject,
                                    EmailMessageSchema.DateTimeReceived
                                    );

            long message_count;
            string text_message;

            while (mail_queue.TryDequeue(out myItem) == true)
            {
                try
                {
                    Email_Message email = new Email_Message();

                    await myItem.Load(nps);

                    email.Body = myItem.Body;
                    email.BodyText = myItem.TextBody;
                    email.From = myItem.From.Name;
                    email.From_Address = myItem.From.Address;

                    mail_bag.Add(email);

                    message_count = mail_bag.Count;

                    if (message_count % this.Update_threshold == 0)
                    {
                        text_message = string.Format("Count: {0} of {1}", message_count, total_item_count);
                        Update_Status(text_message);
                    }

                }

                catch (Exception ex)
                {
                    throw ex;
                }
            }

            return true;
        }

Open in new window

ste5anSenior Developer

Commented:
Where does the exception exactly happens? As you're using a queue, you may need to ensure that no item get lost.
But you should start with the overall logic first:

What kind of queue do you have? What kind of processing do you need? Normally you have a single (or limited amount of) dispatcher (methods) and a pool of workers. Thus having a queue and a single (?) or more workers like you have is imho not the correct approach.
What is your target .NET framework and used VS?

Author

Commented:
Hi ste5an,

I am getting exceptions in the Process_Messages method,

It seems to be if there are two many tasks then I exceed the Exchanges connection limit. From what I read it is 10 concurrent connections.

The background on what I am trying to do is scan around 9000 messages in my mailbox to gather information.

I was putting it in a ConcurrentQueue as then getting tasks to then objects from this queue.

I thought because ConcurrentQueue  is threadsafe this would be the way to do it. My code target is .NET Core 3.1 with Visual Studio 2019
Version 16.4.3

I also think I need to test the debugger again as the code seems to all step in the method at once. (If I can find a way to trace one Task).

I am for the feedback if I am doing it the wrong way.

Can you elaborate a bit more when you say the following:

Normally you have a single (or limited amount of) dispatcher (methods) and a pool of workers. Thus having a queue and a single (?) or more workers like you have is imho not the correct approach.

Any example code you can point me to so I can follow?

Thanks again your help is excellent,

Ward..
Senior Developer
Commented:
As you have a limited resource (Exchange) and exceptions, you need to split your method to get a better, more granular exception handling. E.g. (unstested):

private async Task<Boolean> Process_Messages()
{
    bool result = false
    try
    {
        EmailMessage myItem;
        while (this.mail_queue.TryDequeue(out myItem))
        {
            if (!ProcessMessage(myItem)) {
                // TODO: Logging.
                // TODO: Consider retry logic.
                if (!ProcessMessage(myItem))
                {
                    // Consequtive errors may indicate the necessitity to abort.
                    break;
                }
            }
        }

        result = true;
    }
    catch (Exception ex)
    {
        // TODO: Log exception
    }

    return result;
}

private bool ProcessMessage(EmailMessage myItem) {
    bool result = false;
    try
    {
        PropertySet nps = new PropertySet(EmailMessageSchema.Body, EmailMessageSchema.TextBody, EmailMessageSchema.From, EmailMessageSchema.Subject, EmailMessageSchema.DateTimeReceived);
        long message_count;
        string text_message;
        Email_Message email = new Email_Message();
        await myItem.Load(nps);
        email.Body = myItem.Body;
        email.BodyText = myItem.TextBody;
        email.From = myItem.From.Name;
        email.From_Address = myItem.From.Address;
        mail_bag.Add(email);
        message_count = mail_bag.Count;
        if (message_count % this.Update_threshold == 0)
        {
            text_message = string.Format("Count: {0} of {1}", message_count, total_item_count);
            Update_Status(text_message);
        }

        result = true
    }
    catch (Exception ex)
    {
        // TODO: Log exception
        // TODO: Examine the exception. Maybe it can be handled internally.
    }

    return result;
}

Open in new window

Depending on the logged exceptions, you may consider limiting your tasks to 2, 4 or 8.
AndyAinscowFreelance programmer / Consultant
Commented:

I guess you want things (main app and message processing) to run separately from one another to allow the user to continue with the main app.  

Instead of trying multiple calls to the ProcessMessage maybe you should have just the one thread and handle the messages one after the other rather than simultaneously.  Performing the action that way should stop you running out of email server resources with too many simultaneous calls ( equals no exception thrown? ).  This thread could be run separately from the main app so that is still responsive.

Author

Commented:
Hi AndyAinscow,

I was having multiple tasks to divide and conquer. I am thinking having say 2 or 3 should make it process faster then 1. My testing seems to indicate 10 or less. Is that assumption correct that  say 2 tasks can harve the processing time of one task given the same code etc?

Besides responsiveness to the user making it process more quickly as it can take a while to get through the amount of messages.

Thanks
Ward
ste5anSenior Developer
Commented:
Well, before you entirely change the approach, you should add logging and collect the exception messages. Cause interacting with Exchange may simply require some retry strategies, when processing each message.
AndyAinscowFreelance programmer / Consultant
Commented:

>> I am thinking having say 2 or 3 should make it process faster then 1


It should reduce the time to finish but not necessarily half the time or a third.  Each thread gets a timeslice from the processor then something else if given a timeslice....  Other running apps also get processor time to run their own tasks.  So, overall your app will get proprtionally more processor time but there is also more overhead (not much but some) from your approach.


As Ste5an says find out first what is actually going wrong.  I'm guessing you are having problems because of too many simultaneous calls to the email server.  I might be guessing wrong.

Author

Commented:
Hi AndyAinscow and ste5an,

Great answers and help - much appreciated,

Ward