Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I intercept Task.Factory.StartNew?

Our application has a lot of calls to Task.Factory.StartNew(Action action). Unfortunately, when doing this, the culture is not set, and furthermore, there is no error handling. I began with a starter class that would do both:

public static class TaskBuilder
{
    private static Action<System.Exception> _exceptionCallback;

    public static Task StartNew(Action action, CultureInfo cultureInfo, Action<System.Exception> exceptionCallback)
    {
        _exceptionCallback = exceptionCallback;

        return Task.Factory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);

            action.Invoke();
        }).ContinueWith(t => ManageException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
    }

    private static void ManageException(System.Exception e)
    {
        if (_exceptionCallback != null)
        {
            _exceptionCallback(e);
        }
    }
}

But then I realized that a better approach would be an interceptor. I would like to intercept the call to StartNew so that the new thread contains the culture and error handling code. My attempt at this produced the following code:

public class TaskInterceptionHandler : ICallHandler
{
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        Thread.CurrentThread.CurrentUICulture = // How do I get parent cultureInfo?;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureInfo.Name);

        var methodReturn = getNext().Invoke(input, getNext);

        if (methodReturn.Exception != null)
        {
            // How do I throw the exception to the calling thread here?
        }
        return methodReturn;
    }

    public int Order { get; set; }
}

Here is where I'm stumped. Firstly, how do I get the parent CultureInfo? Secondly How do I return the exception to the calling thread? and How do I use this class in my calls? I.e. how do I replace the existing Task.Factory.StartNew(..)

I'm using Unity, and I'm in unfamiliar territory here. Any help or guidance would be appreciated, or is there even a better solution? Maybe I'm starting on the wrong foot?

I'm using .NET 4.5

Most of the feedback I'm getting below seems to avoid the interceptor route. Is it safe to assume using an interceptor is the wrong way to go? If someone can guide me in that direction, it would allow me to do a comparison. If the answer to the question is yes, I'd like to know how?

like image 533
Ray Avatar asked Aug 29 '14 20:08

Ray


People also ask

What does Task factory StartNew do?

StartNew(Action<Object>, Object, CancellationToken, TaskCreationOptions, TaskScheduler) Creates and starts a task for the specified action delegate, state, cancellation token, creation options and task scheduler.

What is the difference between task run and task factory StartNew?

Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!


1 Answers

Rather than setting the culture in every task, I suggest you set the CultureInfo.DefaultThreadCurrentCulture and CultureInfo.DefaultThreadCurrentUICulture properties to set the culture globally for all future threads.

Regarding error handling, it's probably easier to use await in a try/catch block, rather than passing a delegate to handle the exception:

try
{
    // Task.Run is similar to Task.Factory.StartNew, but easier to use
    await Task.Run(...);
}
catch(Exception ex)
{
    // handle it...
}

BTW, your current error handling mechanism won't work if you have more than one task running at the same time, since there's only one _exceptionCallback for all tasks...

like image 84
Thomas Levesque Avatar answered Nov 01 '22 12:11

Thomas Levesque