3

I have an Application in C#/Winforms that is basically used to run test for customers accounts in a specific order/setup. They are all tests run in browsers and so I built it so it would automate the process since there are about 10-12 tests that need to be ran everytime a customer account is pulled up.

Basically what happens is you input the account number then it runs the tests.

Here's a sample code.

public void startTestProcess()
    { 
        if (!cancelTests)
        {
            testRunning = true;
            var tabPage = createTabPage(banToRun, Convert.ToInt32(banToRun));
            loadingBox.Visible = true;
            mainTabControl.TabPages.Insert(0, tabPage);
            mainTabControl.SelectedTab = mainTabControl.TabPages[0];
            runTest(tabPage, banToRun);
            loadingBox.Visible = false;
        }
    }

private void runTest(TabPage t, string ban)
    {
        if (!cancelTests && !cancelCurrentOnly)
        {
            var tC = createInitialTabControl();
            t.Controls.Add(tC);
            int[] theTests = profileInfo.getSetList;
            for (int i = 0; i < theTests.Length; i++)
            {
                if (!cancelTests && !cancelCurrentOnly)
                {
                    var newTab = createTabPage(urlStrings.getName(theTests[i]), theTests[i]);
                    tC.TabPages.Add(newTab);
                    var webBrowser = createBrowser(urlStrings.getUrl(theTests[i], ban));
                    newTab.Controls.Add(webBrowser);
                    if (theTests[i] != 0 && theTests[i] != 1 && theTests[i] != 6
                        && theTests[i] != 11 && theTests[i] != 12)
                    {
                        if (!webBrowser.IsDisposed)
                        {
                            try
                            {
                                while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
                                {
                                    Application.DoEvents();
                                }
                            }
                            catch
                            {
                                //Do Nothing
                            }
                        }
                    }
                    IntPtr pHandle = GetCurrentProcess();
                    SetProcessWorkingSetSize(pHandle, -1, -1);
                }
            }
        }
        if (cancelCurrentOnly)
        {
            cancelCurrentOnly = false;
        }
        banToRun = string.Empty;
        testRunning = false;

    }

So basically my question is, how can I optimize what I have in order to

A. Reduce lag/freezing - Note: Already implemented a way of forcing garbage collection after each test is run. B. Improve performance of the WebBrowser controls possibly? - Already tried some webbrowser alternatives like WebKit for C# Wrapper (does not work on all tests due to some ajax based coding i believe) C. Maybe implement multi-threaded operations. Not sure how i'd go about this without having cross-threaded exceptions thrown.

Thanks for your assistance. If you have any other questions feel free to ask.

Alex
  • 2,114
  • 9
  • 25
  • 34

2 Answers2

6

The lag / freezing issue is caused by you not using multiple threads; therefore all of the test runs are using the UI thread, so the UI cannot respond while the tests are being run.

If the reason you are not using a backround worker(s) is that your are worried about having cross-threaded exceptions thrown, then you just need to make sure you are properly passing information between your threads (as opposed to avoiding multithreading altogether).

Update

This question addresses the issue of updating the UI based on workers' progress.

As a side note, you should not have to force garbage collection; most of the time, this will actually decrease performance. The garbage collector is specifically designed to collect when it has availability to do so (when the CPU is available). Forcing it to run takes cycles away from the real work your app is trying to do.

Community
  • 1
  • 1
Mark Avenius
  • 13,679
  • 6
  • 42
  • 50
  • I wish that was the case, if I do not force garbage collection that app actually bogs down... a lot. The tests that are ran on these web browser controls are actually very intensive tests so I force it after the browser has finished loading in order to improve performance. – Alex Feb 24 '11 at 15:57
  • @Alex: what size are the pages you are loading? And how much memory is in the machine that is running this? – Mark Avenius Feb 24 '11 at 16:00
  • Not sure about the size of the pages, however its going to be running on multiple machines (about 500 or so) at 2gb's per machine(could be more on some) – Alex Feb 24 '11 at 16:31
  • 1
    @Alex: If you're not sure how to do the multithreading, look into using the BackgroundWorker control. And I bet your problem with garbage collection would go away if you started Dispose()ing objects better. In particular, are you making sure your WebBrowser objects are being disposed correctly? – Daniel Pryden Feb 24 '11 at 16:37
  • I've added a background worker and it's really speed things up. I wasn't sure how to implement it with the UI elements and i found out about how to use invoke. object.Invoke((MethodInvoker)delegate { blah }); – Alex Feb 24 '11 at 18:16
  • @Alex: glad to hear I could help. – Mark Avenius Feb 24 '11 at 18:38
  • Oh also forgot to mention i switched out Application.DoEvents(); for System.Threading.Thread.Sleep(100); It reduced the load on the CPU 10 fold and since im no longer in the GUI thread it doesn't lock up. – Alex Feb 24 '11 at 18:48
2

I see a lot of heavy lifting being performed in methods that also handle GUI, and therefore I assume that all of this is being done in the application's main thread (which will block while performing non-graphic operations related to that WebBrowser and other areas).

Try to refactor this application to run in multiple threads. The main thread should be available as much as possible to respond to user events. Background threads should do the heavy lifting of creating expensive objects, performing long-running read/write operations, etc.

KeithS
  • 70,210
  • 21
  • 112
  • 164
  • I agree, however creating these objects that are created in the loops would have to be created in the GUI thread otherwise i could not add the controls to the GUI. So my question is really, what part of this test code could i move into the background threads is what i'm trying to get at i guess. – Alex Feb 24 '11 at 15:52
  • 1
    I would look at moving ALL this logic (involving the looping and test running) into a worker thread, and when a GUI update is needed, use the Control.Invoke() or BeginInvoke() methods to perform JUST the GUI operation back on the UI thread. With the looping back in the worker thread, the main thread should remain active enough to respond to programmatic AND user requests to update the UI. – KeithS Feb 24 '11 at 16:00