The Practical Guide to Multithreading – Part 1


This article does not give an introduction to multithreading, processes, processors, etc. Nor does it gives the syntax details of threading functions. It only presents the practical approach to multi-threaded programming.
If you do not know about multithreading, then read some articles on the web that show the syntax, how you write console applications, and how to use two threads simultaneously writing on the console. Learn about how one finishes before the other and things like that. I am here only concerned about practical approach. Like, “How you cancel or pause the processing?” (one of the simplest!)

Expected Audience:

  • Knows the basics of multithreading.
  • Knows the GUI programming in either MFC or .NET Framework.
  • The example code and images in this article are presented in both C++/C# and MFC/.NET GUIs. The project of your choice is available to download.

    The Practical Example 1

    You are doing some lengthy processing, and displaying a progress-bar along with some text that reflects the processing status.

    And here is code to do the processing:

    private void btnProcess_Click(object sender, EventArgs e)
    {
        int nStart = Convert.ToInt32(txtStart.Text);
        int nEnd = Convert.ToInt32(txtEnd.Text);
    
        prgProcess.Minimum=nStart;
        prgProcess.Maximum=nEnd;
        for (int nValue = nStart; nValue <= nEnd; nValue++)
        {
            lblStatus.Text = "Processing item: "+ Convert.ToString(nValue);
            prgProcess.Value = nValue;
        }
    }

    As you can see, it seems to perform well, but not as expected. The Status text isn’t being updated (download and run the code). Furthermore if you enter high value like in following image, and then try to fiddle with the window, the window does not respond! You may see something like what is shown below (depending on your OS):

    So, how do you solve this problem? The simplest and one of the worst solution is do call Application.DoEvents (In C#). MFC/Windows API programmers can call PeekMessage and DispatchMessage sequence for the same, but not shown here for simplicity:

    for (int nValue = nStart; nValue <= nEnd; nValue++)
    {
        lblStatus.Text = "Processing item: "+ Convert.ToString(nValue);
        prgProcess.Value = nValue;
    
        // Let Windows (application) process user actions (and other messages)
        Application.DoEvents();
    
    }

    In this code, you can see it works flawlessly!

    Well. So, then, why do you need multithreading if this code solves the problem? Well! There are lot of issues!

    Let’s start with simple one. You can re-click the button to start processing (and even change start/end values). This will move the progress bar backwards or forwards depending on values chosen. It would also be random!

    So what? Just disable all of the controls until the processing is done, and then re-enable controls!

    At the initial stages of this kind of programming, it would work most of the time, thus another thread would not be needed. But, when the routine (function) is performing some advanced tasks—like reading from file, encrypting/decrypting contents of file, validating the words in file, sending them over Internet, or just copying to anther file—this would not work. You may need to call DoEvents (PeekMessage/DispatchMessage) multiple times:

    ReadFile();
    DoEvents();
    VerifyContents();
    DoEvents();
    DecryptFile();
    DoEvents();
    bool bDuplicate = AreWordsDuplicate();
    DoEvents();
    if(bDuplicate)
    {
     ProcessDuplicate();
     DoEvents();
    }
    else
    {
    ...
    }
    ...
    

    As you can see from the above code, calling DoEvents at multiple phases of a routine is overkill. It will definitely slow down the processing. Also, you need to keep track of putting DoEvents calls properly. What if user has clicked ‘Cancel’ (assume you left it enabled)? You would then need to have some global, class-level variable to check if the user has canceled, and this must be done after each call to DoEvents.

    At times you also might not be able to disable controls, and a set of control might trigger another event. For example, if you have ‘CopyAllFiles’, which is a long routine, and you put a set of properly balanced DoEvents and ‘HasUserPressedCancel’ calls. But what if user presses ‘DeleteFile’, ‘RenameFile’ or something like that? You cannot, absolutely cannot, control user actions. Furthermore, you are restricting users to use your application efficiently. Just for the sake you want to avoid multithreading?

    Another problem! (Yes, I know there are readers who are justified with MT, but I must put it for completeness!)

    Calling DoEvents can cause a stack overflow exception to occur! This would crash the application. You might wonder why that would happen. Well, DoEvents searches for the events for window (events can be initiated by the user or by the system). If it finds an event (in Windows terms, it is the Windows-Message), it calls respective event/message-handler (function), which might also be calling DoEvents, which might also call something esle. This process might go on, and on and… Duh! Exception! Your application is down!

    Period.

    End of Story.

    Do not use DoEvents, or equivalent variant in other language/framework

    Let’s get hands dirty with Multithreading!

    You might be extremely willing to see how threads are created, managed. The actual code! Hold on your breath for a while. The following paragraph is very important! Read.

    Windows and Threads are different concepts, but for now assume thy are same. A Windows window may process the message synchronously or asynchronously.

  • Synchronously: When you notify a window to process the message/event, and do not return back until the message is processed. In Windows API/MFC, you notify the window (receiver of message) by using SendMessage API. SendMessage will not return until the message is processed by window. In .NET you achieve the same by Control.Invoke method.
  • Asynchronously: You notify the window, but do not wait if message is processed by window. You achieve the same by using PostMessage API. The PostMessage API will put the specified message into target window’s message queue (receiver, who is going to handle the message). You do not manage the message-queue, it is managed by Windows OS. In .NET, you use Control.BeginInvoke method for the same.

    Managing Threads

    Following table lists out common operations with threads and how you achieve them using different programming elements. This list does not include communicating with thread while it is running, nor it lists thread-security, thread-priority etc. POSIX threads are not used in this article, but listed here for completeness.

























    What / Where Windows API MFC Library.NET FrameworkPOSIX Threads
    Creating threadCreateThread AfxBeginThreadSystem.Threading.Thread.Startpthread_create
    Waiting for thread to finishWaitForSingleObject WaitForSingleObject on CWinThread::m_hThreadThread.Joinpthread_join
    Suspending threadSuspendThread SuspendThread API, or CWinThread::SuspendThread Thread.Suspend*
    pthread_suspend_np**
    Resuming threadResumeThread ResumeThread API, or CWinThread::ResumeThread Thread.Resume*
    pthread_resume_np**
    Suspending for specified timeSleep Sleep API Thread.Sleep
    No appropriate function^
    Forcefully Terminating# TerminateThread TerminateThread API on CWinThread::m_hThread Thread.Abort pthread_kill
    Gracefully Ending thread ExitThread ExitThread API, or AfxEndThread No appropriate method pthread_exit
    Check if thread is running GetExitCodeThread==259 GetExitCodeThread==259 on m_hThread Thread.IsAlive No appropriate function^^
    Retrieve thread’s exit code GetExitCodeThread GetExitCodeThread on CWinThread::m_hThread No appropriate method Only pthread_join (inappropriate)


    * The C# compiler would warn about these methods, that we should use other techniques. And we would, as I would describe later!
    ** These are non-portable APIs, not officially supported by POSIX.
    # Forceful thread termination is not recommended. Rarely, we may need to do that. We’ll discuss it later.

    ^ sleep, or usleep functions suspends the entire process!
    ^^ pthread_kill with ‘0’ signal is not appropriate. Other thread with same ID might have been created. We need to use pthread_key_* set of functions. Describing them is absolutely out of scope of this article.

    Adding in favor of Graceful ending threads: It is always recommended to let the thread function (the root function of thread, like ‘main’ function for the process) return and end the thread properly. Calling the respective functions (as listed) for End-Thread-Gracefully has some issues. Few of them could be: Open file may not be closed, C++ class-objects would not be destroyed via destructor, thread-synchronization object would be inconsistent (later on this), and other issues.

    I assume basic understanding of threads from the reader, but I still mention few points:

  • A thread runs in the process address space. The process has at least one thread. The first thread created at application startup is primary thread.
  • Each thread has its own (function) call stack. Even if you create multiple threads of the same name (i.e. same function/thread-routine), they would have different call stacks.
  • Threads may have one of few ‘states’, like Running, waiting for I/O, blocked etc.
  • A thread that causes exception, like divide by zero, or null-pointer assignment; which is not handled by thread, would take the entire process down!

    Okay! Here is the code to create threads. The simplest approaches are chosen.

    MFC:

    UINT Example1Thread(void*);
    
    void CExample1_Dlg::OnBnClickedOk()
    {
    	AfxBeginThread(Example1Thread, this);
    
    	// OnOK();
    }
    
    UINT Example1Thread(void *pParam)
    {
    	CExample1_Dlg* pThis= (CCExample1_Dlg*)pParam;
    
    	// Use pThis
    	// Perform processing
    
    	return 0;
    }

    Please note the syntax of ‘Example1Thread’. AfxBeginThread requires the function-pointer, which returns ‘UINT’ and takes ‘void*’. The void* argument content is same what we pass as second argument to AfxBeginThread. Since we know the exact type, we further typecast it to C++ class type.

    Is must be noted that AfxBeginThread/CreateThread requests the Operating System to create the thread and eventually run it. Thread creation function does not wait the thread-routine to finish (Otherwise there is no meaning of multithreading!)

    C# / .NET:

    // using System.Threading;
    private void btnStartThreaded_Click(object sender, EventArgs e)
    {
        Thread MyThread = new Thread(ProcessRoutine);
        MyThread.Start();
    }
    void ProcessRoutine()
    {
        // Do processing here.
        // We better make this method 'static'
        // But we'll take those things later.
        // Here we can use all member of class directly (since we have 'this')
    }

    Fine! We now put the code in ‘ProcessRoutine’ which was in ‘btnProcess_Click’ (see first code above). When we run (with F5), Visual Studio launches new world for us! It interrupts the debugging session, and shows Cross Thread Access exception:

    If you run it without debugging (i.e. using Ctrl+F5), the program would run and would work as expected. But the program may crash down at any time. The similar thing would happen for MFC based application. The debugger may show some assert at some remote location, showing some alien code of MFC library. Remember that MFC window classes are not thread safe.

    Why does it happen?

    You know that you cannot open the same file in write-mode simultaneously, and change it contents (ignore advanced ways to do that, if you know!). Files, windows, sockets, data-buffers, screens (and other devices), database tables etc. are some kind of Objects. These objects, mostly, prevent simultaneous modification to them. The reason is simple: Consistency!
    In general, you perform modification to objects in following pattern:

    1. Wait for the object to become free for modification
    2. Open object to modify
    3. Modify the contents of object
    4. Close the object, so that others can open it.

    You may list some type of objects (like database tables), that allows simultaneous modification. Well, they internally use multithreading concepts, which you would get to know soon!

    What it has to do with the current problem in hand

    The windows (or say controls) are created by some thread, generally by primary thread of application, but that is not necessary. That thread holds the ‘write’ access to modify the contents. We are trying to modify the contents using other thread, which violates the rule of ‘consistency’ we discussed previously. We also cannot use 4-step process (wait, open, modify, close) to amend the contents of window (object). Closing the window object would literally mean destroying the window (control).
    Please note that modifying window-object does not mean changing the text/caption only, but includes modifying any of its properties (like color, shading, font, size, orientation, visibility etc) also.

    So, what can be done?

    We can send or post a message to the window! If you can recall correctly Sending the message (via SendMessage/Invoke) is synchronous operation. It means you notify a window, and wait the target window to finish the operation. For example you can send WM_SETTEXT message from other window to set the text of target window. Don’t get confused over this weird name (WM_SETTEXT) or on the actual usage of SendMessage. Things will come out of blur, as we move on! Calling SendMessage this way is same as calling SetWindowText (see code).

    UINT Example1Thread(void *pParam)
    {
    	CExample1_Dlg* pThis= (CExample1_Dlg*)pParam;
    
    	pThis->SendMessage(WM_SETTEXT, 0,(LPARAM)L"New Caption!");
    
    	// eq. to ::SendMessage(pThis->m_hWnd, WM_SETTEXT, 0,(LPARAM)L"New Caption!");
    
            // pThis->SetWindowText("New Caption!");
            // SetWindowText internally calls SendMessage, which sends WM_SETTEXT message to target window.
    
    	return 0;
    }

    Calling SendMessage from any thread to any window is safe! But it’s not true with MFC objects, and not with .NET control classes. Be aware. They represent complex class-objects, and are not simple handles. Okay, I won’t elaborate more about this here, but would continue to our multithreading session. 🙂

    Continuing with the problem…
    Now let us update the progress bar and status-text in properly threaded manner:

    // Delegates - same as function pointers.
    // First line declares type, second declares variable
    delegate void DelegateType(int x);
    DelegateType TheDelegate;
    
    // We store the Start and End values in class-variable, so that 'thread' actually does only processing.
    int StartFrom, EndTo;
    private void btnStartThreaded_Click(object sender, EventArgs e)
    {
        // Set the delegate.
        TheDelegate = MessageHandler;
    
        StartFrom = Convert.ToInt32(txtStart.Text);
        EndTo = Convert.ToInt32(txtEnd.Text);
    
        prgProcess.Minimum = StartFrom;
        prgProcess.Maximum = EndTo;
    
        // Disable button, so that user cannot start again.
        btnStartThreaded.Enabled = false;
    
        // Setup thread and start!
        Thread MyThread = new Thread(ProcessRoutine);
        MyThread.Start();
    }
    
    // This is delegated function, runs in the primary-thread (i.e. the thread that owns the Form!)
    void MessageHandler(int nProgress)
    {
        lblStatus.Text = "Processing item: " + Convert.ToString(nProgress);
        prgProcess.Value = nProgress;
    }
    
    void ProcessRoutine()
    {
        for (int nValue = StartFrom; nValue <= EndTo; nValue++)
        {
            // Only actual delegates be called with Invoke, and not functions!
            this.Invoke(this.TheDelegate, nValue);
        }
    }

    I request you to study about delegates. In short, delegates are like function-pointers in C/C++. You set some delegate-variable to appropriate type of function. Further you call Invoke or BeginInvoke, that would eventually call the function in the context of thread to which the control belongs. Remember all controls (forms, comboboxes, progress bars etc) are derived from System.Control).
    In the sample above, I have disabled the button so that user cannot start processing again.

    The MFC approach:

    UINT Example1Thread(void*);
    
    void CExample1_Dlg::OnBnClickedOk()
    {
    	// Get start and end values
    	StartFrom = GetDlgItemInt(IDC_START);
    	EndTo = GetDlgItemInt(IDC_END);
    
    	// Set progress bar range
    	ProgressBar.SetRange(StartFrom, EndTo);
    
    	// Disable button
    	GetDlgItem(IDOK)->EnableWindow(FALSE);
    
    	// Start off the thread
    	AfxBeginThread(Example1Thread, this);
    }
    
    // The thread message handler (WM_MY_THREAD_MESSAGE)
    // -- #define WM_MY_THREAD_MESSAGE	WM_APP+100
    // -- ON_MESSAGE(WM_MY_THREAD_MESSAGE, OnThreadMessage)
    LRESULT CExample1_Dlg::OnThreadMessage(WPARAM wParam, LPARAM)
    {
    	int nProgress= (int)wParam;
    
    	// update progress bar
    	ProgressBar.SetPos(nProgress);
    
    	CString strStatus;
    	strStatus.Format("Processing item: %d", nProgress);
    
    	// update status text
    	StatusText.SetWindowText(strStatus);
    
    	return 0;
    }
    
    
    UINT Example1Thread(void *pParam)
    {
    	CExample1_Dlg* pThis= (CExample1_Dlg*)pParam;
    
    	for (int nValue = pThis->StartFrom; nValue <= pThis->EndTo;  ++nValue)
    		pThis->SendMessage(WM_MY_THREAD_MESSAGE, nValue);
    
    	return 0;
    }

    The thread is calling SendMessage with WM_MY_THREAD_MESSAGE message (custom message). This, in turn, calls OnThreadMessage in the primary thread's context, and thus updating controls is thread-safe!

    (For MFC/WinAPI programmers: Though this example may be starting point for you to understand and use SendMessage, ON_MESSAGE and defining/handling user-messages; and you might find it easy. I request you to read some material, try some stuff yourself!)

    Here it looks like:

    The last note before we move on to two more practical examples. The approach chosen here (in both C++ and C#) is not appropriate.

  • Firstly, we are accessing integer variables into another thread. We can pass these variables to thread before thread starts, that I will elaborate in next part.
  • Secondly, we are still calling SendMessage/Invoke instead of PostMessage/BeginInvoke, which actually means target thread must finish before sender thread can continue further. (More on this in next page
  • Thirdly, after the thread finishes, we are not letting the user to start processing again; since we disabled the button, and did not enable it.
  • Fourthly, while thread is running, user is able to close the window and that may raise exception, crash the process!

    Now move on to next page where we would handle these four issues, along with allowing user to Cancel (practical example 2), and allowing user to Pause/Resume too (example 3)!

  • More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read