Tjafaas
Tjafaas

Reputation: 129

C# WinForm application UI freezes after long period

I have developed an Winform UI with C# on .NET 2.0 (EDIT : I could migrate it to 3.5 but not sure ... because server environnement ><).
The long-run operation is encased withing a backgroundworker as it should be, and it regularly gives information back to be shown in the UI via the event "ProgressChanged". So far, so good.

My problem is the following : the long-term operation is potentially infinite (it sends information on a socket) so the user has to stop it through a "Cancel button". Alas, the UI becomes unresponsive, kind of frozen, something like after more than 1 hour of running.

I have tried to set a timer that forces a refresh on the UI every 5 minutes but this doesn't seem to help ...

My "main" class has the [STAThread] attribute by the way.

How can I solve this ?

EDIT - the code in the WinForm looks like this :

public MainForm()
{
    InitializeComponent();

    this._bw = new BackgroundWorker();
    this._bw.WorkerReportsProgress = true;
    this._bw.WorkerSupportsCancellation = true;
    this._bw.DoWork += new DoWorkEventHandler(_bw_DoWork);
    this._bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged);
    this._bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted);
    //... more initialization
}

private void btn_sendMessagesContinuous_Click(object sender, EventArgs e)
{
    //Argument construction
    BackgroundWorkerArgument bwArgument = this.CheckParametersAndCreateBwArgument(BackgroundWorkerTaskEnum.ConnectionContinuous);

    //Launch
    if (this._bw.IsBusy == false)
    {
        //Activating ProgressBar and CancelButton
        this.SetControlsBeforeWork();
        this._bw.RunWorkerAsync(bwArgument);
    }
}

private void _bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    RmessService rmessService = null;

    //Timer for GUI refresh
    _refreshTimer.Start();

    //Getting the arguments
    BackgroundWorkerArgument bwArgument = e.Argument as BackgroundWorkerArgument;
    BackgroundWorkerTaskEnum bwTask = bwArgument.BackgroundWorkerTaskEnum;

    try
    {
        switch (bwTask)
        {
            case BackgroundWorkerTaskEnum.ConnectionContinuous:

                rmessService = new RmessService(worker);
                bwArgument.ServiceMakesReports = true;

                rmessService.ConnectToRmess(bwArgument);

                while (worker.CancellationPending == false && rmessService.IsStillConnected())
                { 
                        // Used to lower the processor usage
                        System.Threading.Thread.Sleep(100); 
                }

                break;

            case ...

        }
    }
    catch (Exception ex)
    { //... do the log and stuff ... }
    finally
    {
       if (rmessService != null)
       { rmessService.Dispose(); }
    }

Now as for the service, I choosed to pass along the backgroundWorker into the constructor of my service. I know, should have probably gone through events inside the service, but that's not the point ... hopefully ?

Therefore, I have something like this in the service :

    ///Constructor 
    public RmessService(BackgroundWorker backgroundWorker)
    {
        this._bwMainForm = backgroundWorker;
        this._dicoThreadSocketConnection = new Dictionary<BackgroundWorker, bool>();
    }

    ///Main method of the service
    public void ConnectToRmess(BackgroundWorkerArgument bwArguments)
    {
        this._bwMainForm.ReportProgress(0, new BackgroundWorkerProgressState(BackgroundWorkerProgressEnum.SetProgressInformation, "Test de connexion au socket en cours ..."));

        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            socket.Connect(bwArguments.AdresseIP, bwArguments.Port);
            Thread.Sleep(100);
        }
        finally
        {
            if (socket.Connected)
            { socket.Disconnect(false); }
        }

        this._bwMainForm.ReportProgress(0, new BackgroundWorkerProgressState(BackgroundWorkerProgressEnum.SetProgressInformation, "Envoi des messages en cours ..."));
        int index = 0;
        long idBoitier = bwArguments.IdBoitier;
        while (index < bwArguments.NombreConnexions)
        {
            BackgroundWorker bwSocket = new BackgroundWorker();
            bwSocket.WorkerReportsProgress = true;
            bwSocket.WorkerSupportsCancellation = true;
            bwSocket.DoWork += new DoWorkEventHandler(bwSocket_DoWork);
            bwSocket.ProgressChanged += new ProgressChangedEventHandler(bwSocket_ProgressChanged);
            bwSocket.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwSocket_RunWorkerCompleted);

            //Ajout du BW à la liste
            this._dicoThreadSocketConnection.Add(bwSocket, false);

            //Construction des arguments à passer
            List<object> listArguments = new List<object>() { bwArguments, idBoitier };
            bwSocket.RunWorkerAsync(listArguments);
            Thread.Sleep(100);

            //Incrémentation
            idBoitier++;
            index++;
        }
    }

    private void bwSocket_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //Simple message transfer
        this._bwMainForm.ReportProgress(0, e.UserState);
    }

EDIT 2 : So here's the rest of the code.

    private void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        BackgroundWorkerProgressState bwProgressState = e.UserState as BackgroundWorkerProgressState;

        switch (bwProgressState.BackgroundWorkerProgressEnum)
        {
            case BackgroundWorkerProgressEnum.SetProgressInformation:
                this.lbl_progressInfo.Text = bwProgressState.State as string;
                break;

            case BackgroundWorkerProgressEnum.AddSocketProgressMessage:
                this.richTextBox_generalInfo.Text += (bwProgressState.State as string) + "\r\n";
                break;

            default:
                break;
        }
    }

    private void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Stopping ProgressBar animation
        this.SetControlsAfterWork();
    }

Upvotes: 2

Views: 1922

Answers (1)

Tjafaas
Tjafaas

Reputation: 129

Seems like previous comment made by @groverboy was the simple solution: I was regularly updating a "richTextBox" (that acted as an "instant log") with some information ... too much unfortunately.

The frequence update was less of a problem than the sheer number of added string. Though if I start with many sockets, the update frequency may become a problem as well.

I solved it with one of the many possible ways: update less information. One sentence logged in a minute instead of ~1000 per minute.
Of course, the best way of keeping a log would have been to put it inside a file. In my case, the log serves no other purpose than to say immediately "all is fine". Another way would have been to cull down the text in the richTextBox.

Thank you for the help!


EDIT -
I'll explain where my problem came from: initially my software could test socket connections on a specific IP:port. The receiving service at the given IP is supposed to handle multiple connections. For one connection, you can have multiple sendings (and for one sending, multiple messages). For each sending, i would write in the log, that is the "richTextBox" in the GUI: "informer n° X send Y correctly".
No problem for this scenario ... as long as your connections number and sendings per connection are defined. The software evolved as to be able to keep the connections, but have a limitless number of sendings.

In this last case, the text would grow too quickly (one connection making roughly 10 sendings, so that's 10 messages) with even just a small connections number.
I have tested a text reset in TextChanged event when the length goes above 10.000 caracters: there was no problem at all with the same settings that made my GUI freeze.
This leads me to think the string length was the main problem, though the frequency of updates could have made things worse as well.

Upvotes: 1

Related Questions