Abhash786
Abhash786

Reputation: 903

UI should not be freeze during background processing

I am working on legacy application (winform App) where I need to improve performance.

In this application, we are using MVP pattern and Shell usage reflection to find which presenter it needs to call in order to satisfied the user request. So there is a function which do the following tasks...

  1. Find the appropriate presenter
  2. Itrate through it's all methods to find out default method reference.
  3. prepare an array for method's parameter inputs
  4. Call the default method on presenter
  5. return the presenter reference

here is some code...

 public object FindPresenter(Type pType, string action, Dictionary<string, object> paramDictonary, string callerName = null)
        {
            if (pType == null)
            {
                throw new ArgumentNullException("presenterType");
            }
            var presenterTypeName = pType.Name;

            var presenter = _presenterFactory.Create(pType);
            presenter.CallerName = callerName;

            if (presenter == null)
            {
                throw new SomeException(string.Format("Unable to resolve presenter"));
            }

            // Check each interface for the named method
            MethodInfo method = null;
            foreach (var i in presenter.GetType().GetInterfaces())
            {
                method = i.GetMethod(action, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
                if (method != null) break;
            }

            if (method == null)
            {
                throw new SomeException(string.Format("No action method found"));
            }

            // Match up parameters
            var par = method.GetParameters();
            object[] results = null;
            if (paramDictonary != null)
            {
                if (par.Length != paramDictonary.Count)
                    throw new ArgumentException(
                        "Parameter mis-match");

                results = (from d in paramDictonary
                           join p in par on d.Key equals p.Name
                           orderby p.Position
                           select d.Value).ToArray();

            }

            // Attach release action
            presenter.ReleaseAction = () => _presenterFactory.Release(presenter);

            // Invoke target method
            method.Invoke(presenter, results);

            return presenter;
        }

This method take around 15-20 second to complete and freeze the UI. I want to refector this method with some async processing so UI is not freeze during this method. As I need to return presenter reference, I thought of using wait() or join() method, but they will again lock the UI.

Please note, I am using .NET 4.0.

Upvotes: 0

Views: 90

Answers (3)

user3230660
user3230660

Reputation:

Well, based on your comment: "My question is how can I refector this by putting some tasks in background which do not lock the UI."

Try this:

class Program
{
    static void Main(string[] args)
    {
        Task t1 = Task.Run(() => FindPresenter(typeof(Program), "boo"))
            .ContinueWith(x => UsePresenter(x.Result));

        while (true)
        {
            Thread.Sleep(200);
            Console.WriteLine("I am the ui thread. Press a key to exit.");


            if ( Console.KeyAvailable)
                break;
        }

    }

    static object FindPresenter(Type type, string action)
    {
        // Your FindPresenter logic here

        Thread.Sleep(1000);
        return (object)"I'm a presenter";
    }

    static void UsePresenter(object presenter)
    {
        Console.WriteLine(presenter.ToString());
        Console.WriteLine("Done using presenter.");
    }
}

Sorry I am not a Winforms guy.... In WPF we have Dispatcher for thread affinity issues. You might try this: System.Windows.Threading.Dispatcher and WinForms?

Upvotes: 0

Mike Nakis
Mike Nakis

Reputation: 62120

Unless you have millions of presenter types to search through, which is highly doubtful, and unless your average presenter has millions of parameters, which is also highly doubtful, there is nothing in the code that I see above that should take 15 seconds to execute.

So, the entirety of the delay is not in the code that you are showing us, but in one of the functions it invokes.

That could be in the very suspicious looking _presenterFactory.Create(pType); or in the implementation of paramDictionary if by any chance it happens to be a roll-your-own dictionary instead of a standard hash dictionary, or in the invocation of method.Invoke(presenter, results); itself.

Most likely in the last.

So, first of all, profile your code to find what the real culprit is.

Then, restructure your code so that the lengthy process happens on a separate worker thread. This may require that you pull considerable parts of your application out of the GUI thread and into that worker thread. Nobody said GUI programming was easy.

If the culprit is method.Invoke(), then this looks like something that you may be able to move to another thread relatively easily: Start that thread right before returning, and make sure everything that happens with each one of those presenter objects is thread safe.

But of course, these presenters are trying to actually render stuff in the GUI, then you will have to go and refactor all of them too, to separate their computationally expensive logic from the rendering logic, because WinForms can only be invoked from within its own thread.

Upvotes: 1

haraman
haraman

Reputation: 2752

The easiest approach is to use Application.DoEvents(); in your foreach loop to keep UI unlocked during long running processes

You may refer to MSDN system.windows.forms.application.doevents
But before using it you must also read Keeping your UI Responsive and the Dangers of Application.DoEvents

Upvotes: 0

Related Questions