Tag: Multithreading

  • Windows MultiThreading : Producer-Consumer with a Bounded Buffer

    Producer-Consumer with a Bounded Buffer

    A variation of the producer-consumer problem involves multiple producers and consumers sharing a finite-sized buffer. The challenge is to prevent producers from adding when the buffer is full and to prevent consumers from removing when the buffer is empty.

    Explanation

    • emptySlots semaphore keeps track of available slots in the buffer.
    • fullSlots semaphore tracks items in the buffer.
    • mutex ensures mutual exclusion when accessing the buffer.

    These implementations demonstrate the flexibility and power of semaphores, mutexes, and synchronization patterns in solving classical concurrency problems. Let me know if you’d like further explanations or additional examples!

    Implementation

    #include <iostream>
    #include <windows.h>
    #include <thread>
    #include <vector>
    #include <queue>
    
    const int BUFFER_SIZE = 5;
    std::queue<int> buffer;
    
    // Semaphores
    HANDLE emptySlots;
    HANDLE fullSlots;
    HANDLE mutex;
    
    void producer(int id) {
        int item = 0;
        while (true) {
            Sleep(1000); // Simulate production time
            WaitForSingleObject(emptySlots, INFINITE);
            WaitForSingleObject(mutex, INFINITE);
    
            buffer.push(++item);
            std::cout << "Producer " << id << " produced item " << item << ". Buffer size: " << buffer.size() << "\n";
    
            ReleaseMutex(mutex);
            ReleaseSemaphore(fullSlots, 1, NULL);
        }
    }
    
    void consumer(int id) {
        while (true) {
            WaitForSingleObject(fullSlots, INFINITE);
            WaitForSingleObject(mutex, INFINITE);
    
            int item = buffer.front();
            buffer.pop();
            std::cout << "Consumer " << id << " consumed item " << item << ". Buffer size: " << buffer.size() << "\n";
    
            ReleaseMutex(mutex);
            ReleaseSemaphore(emptySlots, 1, NULL);
            Sleep(1500); // Simulate consumption time
        }
    }
    
    int main() {
        emptySlots = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);
        fullSlots = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);
        mutex = CreateMutex(NULL, FALSE, NULL);
    
        if (emptySlots == NULL || fullSlots == NULL || mutex == NULL) {
            std::cerr << "Failed to create semaphores or mutex.\n";
            return 1;
        }
    
        std::vector<std::thread> producers, consumers;
        for (int i = 0; i < 3; ++i) {
            producers.emplace_back(producer, i + 1);
        }
        for (int i = 0; i < 2; ++i) {
            consumers.emplace_back(consumer, i + 1);
        }
    
        for (auto& p : producers) {
            p.join();
        }
        for (auto& c : consumers) {
            c.join();
        }
    
        CloseHandle(emptySlots);
        CloseHandle(fullSlots);
        CloseHandle(mutex);
    
        return 0;
    }
    
  • Multithreading using MFC – Microsoft Foundation Classes

    Multithreading in MFC (Microsoft Foundation Class) can be accomplished using either worker threads or UI threads. Here’s an overview of both approaches and how to implement them in MFC.

    1. Worker Threads

    Worker threads are background threads that don’t interact directly with the user interface (UI). They are useful for performing long-running operations in the background without freezing the main UI.

    Example: Creating a Worker Thread

    You can create a worker thread using the AfxBeginThread function.

    UINT MyThreadFunction(LPVOID pParam)
    {
        // Perform your task in the background
        for (int i = 0; i < 10; ++i)
        {
            // Simulate some work
            Sleep(1000); // Sleep for 1 second
        }
        return 0; // Thread completed
    }
    
    void StartWorkerThread()
    {
        CWinThread* pThread = AfxBeginThread(MyThreadFunction, NULL);
        if (pThread == nullptr)
        {
            AfxMessageBox(_T("Thread creation failed!"));
        }
    }
    • MyThreadFunction: The function that will run on the worker thread.
    • AfxBeginThread: Used to create a new worker thread.

    Communication Between UI and Worker Thread

    If you want the worker thread to communicate with the UI (e.g., to update progress), you should use thread-safe mechanisms like posting messages to the main thread.

    UINT MyThreadFunction(LPVOID pParam)
    {
        CWnd* pWnd = (CWnd*)pParam;
        for (int i = 0; i < 10; ++i)
        {
            // Simulate work
            Sleep(1000);
    
            // Post a message to the main thread to update progress
            pWnd->PostMessage(WM_USER_UPDATE_PROGRESS, i);
        }
        return 0;
    }
    
    // In your dialog class
    afx_msg LRESULT OnUpdateProgress(WPARAM wParam, LPARAM lParam)
    {
        int progress = (int)wParam;
        // Update UI with progress
        return 0;
    }
    
    BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
        ON_MESSAGE(WM_USER_UPDATE_PROGRESS, &CMyDialog::OnUpdateProgress)
    END_MESSAGE_MAP()
    • PostMessage: Sends a message from the worker thread to the UI thread.
    • ON_MESSAGE: Declares a handler for custom messages in MFC.

    2. UI Threads

    UI threads in MFC are threads that have their own message loops. These threads are used when you need to create or manipulate UI elements (such as windows or dialogs) in the new thread.

    Example: Creating a UI Thread

    class CMyUIThread : public CWinThread
    {
        DECLARE_DYNCREATE(CMyUIThread)
    
    public:
        virtual BOOL InitInstance();
        virtual int ExitInstance();
    };
    
    IMPLEMENT_DYNCREATE(CMyUIThread, CWinThread)
    
    BOOL CMyUIThread::InitInstance()
    {
        // Create a dialog or window here
        CDialog myDialog(IDD_MY_DIALOG);
        myDialog.DoModal(); // Modal dialog
        return TRUE;
    }
    
    int CMyUIThread::ExitInstance()
    {
        // Cleanup code here
        return CWinThread::ExitInstance();
    }
    
    void StartUIThread()
    {
        CWinThread* pThread = AfxBeginThread(RUNTIME_CLASS(CMyUIThread));
        if (pThread == nullptr)
        {
            AfxMessageBox(_T("UI Thread creation failed!"));
        }
    }
    • CWinThread: Base class for both worker and UI threads in MFC.
    • InitInstance: Where you initialize any UI components in the thread.
    • ExitInstance: Handles thread cleanup.

    Thread Synchronization

    When multiple threads access shared resources, you should use synchronization primitives such as CRITICAL_SECTION, CMutex, or CEvent to avoid race conditions.

    Example: Using Critical Sections

    CRITICAL_SECTION cs;
    
    void SomeSharedFunction()
    {
        EnterCriticalSection(&cs);
        // Access shared resource
        LeaveCriticalSection(&cs);
    }
    
    void InitializeCriticalSectionExample()
    {
        InitializeCriticalSection(&cs);
    
        // Make sure to delete the critical section once you're done
        DeleteCriticalSection(&cs);
    }

    This ensures that only one thread can access the critical section at a time, avoiding race conditions.

    Summary

    • Worker Threads: Perform background work; use AfxBeginThread to create them.
    • UI Threads: Handle UI components in a separate thread; use CWinThread and AfxBeginThread for creation.
    • Synchronization: Use thread-safe methods like critical sections, mutexes, or events for resource sharing.

    Let me know if you need any further details or specific examples!

  • Difference and Comparison between Process and Thread

    Here’s a side-by-side comparison of processes and threads:

    AspectProcessThread
    DefinitionIndependent program in executionSmallest unit of execution within a process
    Memory SpaceSeparate memory spaceShares memory space with other threads in the same process
    CommunicationRequires Inter-Process Communication (IPC)Easier and faster within the same process
    Creation OverheadHigher (more resources and time needed)Lower (lighter and faster to create)
    Crash ImpactOne process crash doesn’t affect othersOne thread crash can affect the entire process
    Resource SharingDoes not share resources directlyShares process resources (code, data, files)