critsecs.c

来自「一本已经绝版的好书」· C语言 代码 · 共 381 行

C
381
字号
/************************************************************
Module name: CritSecs.C
Notices: Copyright (c) 1995-1997 Jeffrey Richter
************************************************************/


#include "..\CmnHdr.H"                  /* See Appendix C. */
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <stdio.h>             // For sprintf
#include <process.h>           // For _beginthreadex
#include "Resource.H"


//////////////////////////////////////////////////////////////


// Global variables
// g_fTerminate is set to TRUE when the dialog box is
// dismissed. It indicates to the worker threads that they 
// need to terminate. It is volatile because it can change
// at any time.
volatile BOOL  g_fTerminate = FALSE;   

HWND     g_hwnd;
HANDLE   g_hThread[2];  // Counter[0] & Display[1] threads


// The data that needs protecting
TCHAR    g_szNumber[10] = __TEXT("0");

// The critical section used to protect the data
CRITICAL_SECTION g_CriticalSection;


//////////////////////////////////////////////////////////////


// Add a string to a list box.
void AddToListBox (LPCTSTR szBuffer) {
   HWND hwndDataBox = GetDlgItem(g_hwnd, IDC_DATABOX);

   int x = ListBox_AddString(hwndDataBox, szBuffer);
   ListBox_SetCurSel(hwndDataBox, x);

   if (ListBox_GetCount(hwndDataBox) > 100)
      ListBox_DeleteString(hwndDataBox, 0);
}


//////////////////////////////////////////////////////////////


// Thread to increment the protected counter data
DWORD WINAPI CounterThread (LPVOID lpThreadParameter) {
   unsigned int nNumber, nDigit;
   BOOL fSyncChecked;

   while (!g_fTerminate) {
      // Get the status of the Synchronize check box
      // and save it.
      fSyncChecked = 
         IsDlgButtonChecked(g_hwnd, IDC_SYNCHRONIZE);

      if (fSyncChecked) {
         // If the user wants us synchronized, do it.
         EnterCriticalSection(&g_CriticalSection);
      }

      // Convert the string number to an integer and add 1.
      _stscanf(g_szNumber, __TEXT("%d"), &nNumber);
      nNumber++;

      // Convert the new integer back to a string.
      nDigit = 0;
      while (nNumber != 0) {
         // Put a digit into the string.
         g_szNumber[nDigit++] = (TCHAR) 
      (__TEXT('0') + (nNumber % 10));

         // A call to Sleep here tells the system that we want
         // to relinquish the remainder of our time slice to
         // another thread.  This call is needed for 
         // single-CPU systems so that the results of the 
         // synchronization or lack thereof are obvious. 
         // Normally, your programs would NOT call Sleep here.
         Sleep(0);

         // Get ready to get the next digit.
         nNumber /= 10;
      }

      // All digits converted to characters.
      // Terminate the string.
      g_szNumber[nDigit] = 0;

      // Characters were generated in reverse order;
      // reverse the string.
      // Call _strrev if ANSI, Call _wcsrev if Unicode.
      _tcsrev(g_szNumber);

      if (fSyncChecked) {
         // If the user wants synchronization, do it.
         // In earlier versions of this program, I was calling
         // IsDlgButtonChecked as I did earlier instead of 
         // using the fSyncChecked variable. This caused 
         // problems because the user could check or uncheck 
         // the Synchronize check box in between the calls to 
         // EnterCriticalSection and LeaveCriticalSection.
         // This meant that my thread was sometimes leaving a
         // critical section that it had never entered. And my 
         // thread was sometimes entering a critical section
         // that it had never left.
         LeaveCriticalSection(&g_CriticalSection);
      }

      // If the user wants to display something 
      // after each iteration, do it.
      if (IsDlgButtonChecked(g_hwnd, IDC_SHOWCNTRTHRD))
         AddToListBox(__TEXT("Cntr: Increment"));
   }
   return(0);  // We get here when the window is dismissed.
}


///////////////////////////////////////////////////////////////


// Thread to add the current value of 
// the counter (data) to the list box
DWORD WINAPI DisplayThread (LPVOID lpThreadParameter) {
   BOOL fSyncChecked;
   TCHAR szBuffer[50];

   while (!g_fTerminate) {

      // Determine whether the user wants the threads
      // to be synchronized.
      fSyncChecked = 
         IsDlgButtonChecked(g_hwnd, IDC_SYNCHRONIZE);

      if (fSyncChecked)
         EnterCriticalSection(&g_CriticalSection);

      // Construct a string with the string form of the number.
      _stprintf(szBuffer, __TEXT("Dspy: %s"), g_szNumber);

      if (fSyncChecked)
         LeaveCriticalSection(&g_CriticalSection);

      // Add the string form of the number to the list box.
      AddToListBox(szBuffer);
   }
   return(0);  // We get here when the window is dismissed.
}


//////////////////////////////////////////////////////////////


BOOL Dlg_OnInitDialog (HWND hwnd, HWND hwndFocus, 
   LPARAM lParam) {
   HWND hWndCtl;
   DWORD dwThreadID;
   
   // Associate an icon with the dialog box.
   chSETDLGICONS(hwnd, IDI_CRITSECS, IDI_CRITSECS);

   // Save the handle of the dialog box in a global so that 
   // the threads can easily gain access to it.  This must be 
   // done before creating the threads.
   g_hwnd = hwnd;

   // Initialize the critical section.  This must also be 
   // done before any threads try to use it.
   InitializeCriticalSection(&g_CriticalSection);

   // Create our counter thread and let it start running.
   g_hThread[0] = chBEGINTHREADEX(NULL, 0, 
      CounterThread, NULL, 0, &dwThreadID);

   // Create our display thread and let it start running.
   g_hThread[1] = chBEGINTHREADEX(NULL, 0, 
      DisplayThread, NULL, 0, &dwThreadID);


   // Fill the Process Priority Class combo box and select
   // Normal.
   hWndCtl = GetDlgItem(hwnd, IDC_PRIORITYCLASS);
   ComboBox_AddString(hWndCtl, __TEXT("Idle"));
   ComboBox_AddString(hWndCtl, __TEXT("Normal"));
   ComboBox_AddString(hWndCtl, __TEXT("High"));
   ComboBox_AddString(hWndCtl, __TEXT("Realtime"));
   ComboBox_SetCurSel(hWndCtl, 1);  // Normal

   // Fill the Display Thread Priority 
   // combo box and select Normal.
   hWndCtl = GetDlgItem(hwnd, IDC_DSPYTHRDPRIORITY);
   ComboBox_AddString(hWndCtl, __TEXT("Idle"));
   ComboBox_AddString(hWndCtl, __TEXT("Lowest"));
   ComboBox_AddString(hWndCtl, __TEXT("Below normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Above normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Highest"));
   ComboBox_AddString(hWndCtl, __TEXT("Timecritical"));
   ComboBox_SetCurSel(hWndCtl, 3);  // Normal

   // Fill the Counter Thread Priority 
   // combo box and select Normal.
   hWndCtl = GetDlgItem(hwnd, IDC_CNTRTHRDPRIORITY);
   ComboBox_AddString(hWndCtl, __TEXT("Idle"));
   ComboBox_AddString(hWndCtl, __TEXT("Lowest"));
   ComboBox_AddString(hWndCtl, __TEXT("Below normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Above normal"));
   ComboBox_AddString(hWndCtl, __TEXT("Highest"));
   ComboBox_AddString(hWndCtl, __TEXT("Timecritical"));
   ComboBox_SetCurSel(hWndCtl, 3);  // Normal

   return(TRUE);
}


//////////////////////////////////////////////////////////////


void Dlg_OnDestroy (HWND hwnd) {
   // When the dialog box is destroyed, signal the worker
   // threads to terminate.
   g_fTerminate = TRUE;

   // Resume the worker threads in case they're paused.
   ResumeThread(g_hThread[0]);
   ResumeThread(g_hThread[1]);
}


//////////////////////////////////////////////////////////////


void Dlg_OnCommand (HWND hwnd, int id, HWND hwndCtl, 
   UINT codeNotify) {
   
   HANDLE hThread;
   DWORD dw;

   switch (id) {
      case IDCANCEL:
         EndDialog(hwnd, id);
         break;

      case IDC_PRIORITYCLASS:
         if (codeNotify != CBN_SELCHANGE)
            break;

         // User is changing priority class.
         switch (ComboBox_GetCurSel(hwndCtl)) {
            case 0:
               dw = IDLE_PRIORITY_CLASS;
               break;

            case 1:
            default:
               dw = NORMAL_PRIORITY_CLASS;
               break;

            case 2:
               dw = HIGH_PRIORITY_CLASS;
               break;

            case 3:
               dw = REALTIME_PRIORITY_CLASS;
               break;
         }
         SetPriorityClass(GetCurrentProcess(), dw);
         break;

      case IDC_DSPYTHRDPRIORITY:
      case IDC_CNTRTHRDPRIORITY:
         if (codeNotify != CBN_SELCHANGE)
            break;

         switch (ComboBox_GetCurSel(hwndCtl)) {
            case 0:
               dw = (DWORD) THREAD_PRIORITY_IDLE;
               break;

            case 1:
               dw = (DWORD) THREAD_PRIORITY_LOWEST;
               break;

            case 2:
               dw = (DWORD) THREAD_PRIORITY_BELOW_NORMAL;
               break;

            case 3:
            default:
               dw = (DWORD) THREAD_PRIORITY_NORMAL;
               break;

            case 4:
               dw = (DWORD) THREAD_PRIORITY_ABOVE_NORMAL;
               break;

            case 5:
               dw = (DWORD) THREAD_PRIORITY_HIGHEST;
               break;

            case 6:
               dw = (DWORD) THREAD_PRIORITY_TIME_CRITICAL;
               break;
         }
         // User is changing the relative priority 
         // of one of the threads.
         hThread = (id == IDC_CNTRTHRDPRIORITY) ?
             g_hThread[0] : g_hThread[1];

         SetThreadPriority(hThread, dw);
         break;

      case IDC_PAUSE:
         // User is pausing or resuming both threads.
         if (Button_GetCheck(hwndCtl)) {

            SuspendThread(g_hThread[0]);
            SuspendThread(g_hThread[1]);

         } else {

            ResumeThread(g_hThread[0]);
            ResumeThread(g_hThread[1]);

         }
         break;
   }
}


///////////////////////////////////////////////////////////////


BOOL CALLBACK Dlg_Proc (HWND hwnd, UINT uMsg, 
   WPARAM wParam, LPARAM lParam) {
   
   switch (uMsg) {
      chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
      chHANDLE_DLGMSG(hwnd, WM_DESTROY, Dlg_OnDestroy);
      chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
   }
   return(FALSE);
}


///////////////////////////////////////////////////////////////


int WINAPI _tWinMain (HINSTANCE hinstExe,
   HINSTANCE hinstPrev, LPTSTR pszCmdLine, int nCmdShow) {

   chWARNIFUNICODEUNDERWIN95();
   DialogBox(hinstExe, MAKEINTRESOURCE(IDD_CRITSECS), 
      NULL, Dlg_Proc);

   // Wait for both worker threads to terminate 
   WaitForMultipleObjects(2, g_hThread, TRUE, INFINITE);

   // Close our handles to the worker threads
   CloseHandle(g_hThread[0]);
   CloseHandle(g_hThread[1]);

   // The worker threads can no longer be using the 
   // critical section, so delete it.
   DeleteCriticalSection(&g_CriticalSection);

   return(0);
}


///////////////////////// End Of File /////////////////////////

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?