Welcome to RenEvo Sign in | Join | Help

Proper Thread Work with WF

Most inexperienced developers tend to put workload heavy operations on the same thread that the UI runs on. While you can call ‘Application.DoEvents()’ to keep the windows messages flowing, this is not the proper way to keep your UI painted. In this article I will be explaining how to put your workload heavy operations on a separate thread, and how to properly update your form with information you want the user to see.

Let’s start with a simple snippet.

FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();

Depending on the the size of the directory, this operation could potentially take several minutes or more. During this period, your UI cannot paint itself or receive windows messages. This means, your user thinks the application has locked up, or crashed. This is where threads come into play.

        private void MainForm_Shown(object sender, EventArgs e) {
            Thread thread = new Thread(new ThreadStart(ThreadProc));
            thread.IsBackground = true;
            thread.Start();
        }

The code above simply starts a new thread that runs on the ‘ThreadProc’ method. This will allow our form to run its own code, while the thread is running. Essentially with the result that we can run our workload heavy operations, without hindering the form. Below is the ‘ThreadProc’ method, which contains our workload heavy operation to simply read a directory and its files.       

        /// <summary>
        /// Method to process our workload
        /// </summary>
        private void ThreadProc() {
            FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();
            UpdateActionDelegate uxad = new UpdateActionDelegate(UpdateAction);

            for (int i = 0; i < files.Length; i++) {
                DateTime time = DateTime.Now;

                if ((time - m_Time).Milliseconds >= 50) {
                    m_Time = time;

                    // Now we can update the thread since
                    // we waited the 50ms
                    uxAction.Invoke(uxad, files[ i ].Name);

                    // We could also invoke the mainform directly
                    // and have access to all of the controls
                    //this.Invoke(uxad, item.Name);

                    // Just an example since we are not really processing
                    // anything, so we want to see whats happening
                    Thread.Sleep(250);
                }
            }
        }

There are two key points to this method body.

1. We use a delegate called UpdateActionDelegate, which we use to properly invoke a label named ‘uxAction’ on the form, to safely update its text.


2. We use a private member named ‘m_DateTime’ of type DateTime, which we use to check if 50ms of time has passed between each iteration in the for-loop.

The 50ms is simply a delay. If you have no delay, the systems CPU will show it cycling at 100%, and depending on the clients system, they may have intense flickering on the form. This occurs because the operation would normally try to update the text of the label faster than the form can paint itself. This is why we implemented a 50ms delay before updating the label again. Below is the full code of the MainForm class, which demonstrates this in action. I have also attached the project files in a ZIP.

// *********************************************************************
// [RenEvo Software & Designs]
// [RenEvo], [Proper thread work with WF]
//
//   THIS FILE IS PROVIDED "AS-IS" WITHOUT ANY WARRANTY OF ANY KIND. ANY
//   MODIFICATIONS TO THIS FILE IN ANY WAY ARE YOUR SOLE RESPONSIBILITY.
//
// [Copyright (C) RenEvo Software & Designs  All rights reserved.]
// *********************************************************************

namespace ThreadUIExample {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Collections;
    using System.IO;
    using System.Threading;

    public partial class MainForm : Form {
        public MainForm() {
            InitializeComponent();
        }

        private void MainForm_Shown(object sender, EventArgs e) {
            Thread thread = new Thread(new ThreadStart(ThreadProc));
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// Used to check if 50ms of time has passed since the last file was read
        /// </summary>
        private DateTime m_Time = DateTime.Now;

        /// <summary>
        /// Delegate used to safely invoke the action label
        /// </summary>
        /// <param name="action"></param>
        private delegate void UpdateActionDelegate(string action);

        /// <summary>
        /// Callback used to safely update the text of the action label
        /// when the label is properly invoked
        /// </summary>
        public void UpdateAction(string action) {
            uxAction.Text = string.Format("Action: {0}", action);
        }

        /// <summary>
        /// Method to process our workload
        /// </summary>
        private void ThreadProc() {
            FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();
            UpdateActionDelegate uxad = new UpdateActionDelegate(UpdateAction);

            for (int i = 0; i < files.Length; i++) {
                DateTime time = DateTime.Now;

                if ((time - m_Time).Milliseconds >= 50) {
                    m_Time = time;

                    // Now we can update the thread since
                    // we waited the 50ms
                    uxAction.Invoke(uxad, files[ i ].Name);

                    // We could also invoke the mainform directly
                    // and have access to all of the controls
                    //this.Invoke(uxad, item.Name);

                    // Just an example since we are not really processing
                    // anything, so we want to see whats happening
                    Thread.Sleep(250);
                }
            }
        }
    }
}
Published Monday, December 22, 2008 5:34 PM by Dave Anderson

Attachment(s): ThreadUIExample.zip

Comments

# re: Proper Thread Work with WF

Just FYI, you can just use a Sleep of 1 millisecond to prevent the 100% cpu usage :)

Tuesday, December 23, 2008 12:05 PM by Tom Anderson
Anonymous comments are disabled