VC++技术内幕(第四版)笔记(第10~11章)

第十章:位图

1,Windows的位图实际上是一些和显示象素相对应的位阵列。

2,GDI位图是设备相关位图,用MS基本类库(MFC)中的CBitmap类表示的,依赖具体的设备。

3,DIB社设备无关位图,比GDI位图有许多编程优势。任何运行Windows的机器都可以处理DIB位图。DIB位图通常以.BMP文件形式保留在磁盘中,或作为资源保存在程序的EXE或DLL文件中。WIN32API只直接支持DIB格式文件。

4,Windows在位图中使用模糊颜色。GDI位图也是GDI对象,使用时先创建(并初始化),再把它选入设备环境,使用完后还要删除掉。
注意:
使用GDI位图的时候,不能直接把位图选进显示设备环境或打印机设备环境中。必须利用CDC::CreateCompatibleDC函数为位图创建一个特殊的内存设备环境,然后使用CDC::StretchBlt或CDC::BitBlt函数把内存设备环境中的各个位复制到真正的设备环境中。
说明:
1)CDC::CreateCompatibleDC 
virtual BOOL CreateCompatibleDC( CDC* pDC );
// Creates a memory device context that is compatible with the device specified by pDC. A memory device context is a block of memory that represents a display surface. It can be used to prepare images in memory before copying them to the actual device surface of the compatible device.
2)CDC::BitBlt 
BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );
//Copies a bitmap from the source device context to this current device context.
3)CDC::StretchBlt
BOOL StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop );
//Moves a bitmap from a source rectangle and device into a destination rectangle, stretching or compressing the bitmap if necessary to fit the dimensions of the destination rectangle.
4)应用事例:
void CGDIView::OnDraw(CDC* pDC)
{
 CBitmap bitmap;
 bitmap.LoadBitmap(IDB_BITMAP1);
 CDC *pdcm=new CDC;
 pdcm->CreateCompatibleDC(pDC);
 pdcm->SelectObject(&bitmap);
 pDC->BitBlt(100,100,54,96,pdcm,0,0,SRCCOPY);
 delete pdcm;
 //pDC->StretchBlt(100,100,500,50,pdcm,0,0,50,50,SRCCOPY);
}
注:事例中显示映射模式为MM_TEXT,MM_TEXT下每个位图象素都映射到一个显示器象素。如果在其它的模式下需要进行一些伸缩变换处理。处理缩小的位图时候可以调用CDC::SetStretchBltMode函数(参数可选COLORONCOLOR)使缩小的位图显示更好些。
在非MM_TEXT模式下,使用BitBlt或StretchBlt函数,如果GDI要对其进行伸缩变换,则更新的速度较慢.可以使用以下代码代替BitBlt语句可加快其更新速度:
pDC->SetMapMode(MM_LOENGLISH);//设置映射模式为MM_LOENGLISH。
CSize size(54,96);
pDC->DPtoLP(&size);
pDC->StretchBlt(0,0,size.cx,-size.cy,dcm,0,0,54,96,SRCCOPY);//MM_LOENGLISH模式下注意-size.cy前的负号不能少。


5,事例EX10A中函数说明:
1)CGdiObject::GetObject
int GetObject( int nCount, LPVOID lpObject ) const;
参数说明:
nCount:Specifies the number of bytes to copy into the lpObject buffer.
lpObject:Points to a user-supplied buffer that is to receive the information.
//Return Value:The number of bytes retrieved; otherwise 0 if an error occurs.
//Fills a buffer with data that describes the Windows GDI object attached to the CGdiObject object.
//
Object  Buffer type
CPen   LOGPEN
CBrush   LOGBRUSH
CFont   LOGFONT
CBitmap  BITMAP
CPalette  WORD
CRgn   Not supported
//If the object is a CBitmap object, GetObject returns only the width, height, and color format information of the bitmap. The actual bits can be retrieved by using CBitmap::GetBitmapBits.

2)CGdiObject::GetSafeHandle
HGDIOBJ GetSafeHandle( ) const;
//Return Value:A HANDLE to the attached Windows GDI object; otherwise NULL if no object is attached.


6,事例EX10B说明:位图有程序自己生成,并且能使位图在屏幕上平滑移动。原理:首先将所选择的位图绘制在内存设备环境中,然后再将它反复快速地在屏幕上进行显示。

7,DIB编程(出于个人需要,这里做概要笔记,详见P190-204)
1)在MFC库中有GDI位图的类CBitmap,但没有DIB的类。
2)DIB包含一个二维数组,数组的元素为象素。
3)DIB是标准的Windows位图格式,且BMP文件包含一个DIB结构。
4)DIB访问函数:
SetDIBitsToDevice//直接在显示器或打印机上显示DIB,不进行缩放,位图每一位对应一显示象素或一打印点。
StretchDIBits//仿照StretchBlt类似的方式将DIB显示在显示器或打印机上。
GetDIBits//函数利用申请到的内存,由GDI位图来构造DIB。
CreateDIBitmap//由DIB来创建GDI位图。
CreateDIBSection//创建一种称为DIB项的DIB,然后返回一个GDI句柄。提供了DIB和GDI位图最好的特性。可以直接访问DIB的内存,还可以在DIB中调用GDI函数画图。
5)LoadImage//The LoadImage function loads an icon, cursor, or bitmap.
LoadImage可以直接从一个磁盘文件中读入位图
6)DrawDibDraw//The DrawDibDraw function draws a DIB to the screen.
DrawDibDraw需要MM_TEXT坐标和MM_TEXT映射模式。


8,在按钮上设置位图
1)先设置按钮的Owner Draw属性,然后在对话框类中写一个消息控制函数,在该按钮控制的窗口里画图。
2)使用MFC的CBitmapButton类实现在按钮在设置位图。步骤如下:
第一步:在对话框中添加按钮,并设置Owner Draw属性,大小并不重要。(框架自动调整为位图大小)
第二步:添加位图资源(.bmp),并用相应的按钮Caption+D/U来标识位图。即在位图的属性ID栏中输入"按钮Caption+U"或"按钮Caption+D",其中名字两边的引号不能少,这样就表示了用名字标识资源而不是ID,U表示不翻转显示的位图,D表示翻转显示的位图。如:按钮Caption为[Button1],则在该按钮上贴位图的名字应为["Button1D"]或["Button1U"]。
第三步:为要设置位图的按钮,对应在按钮所在对话框类中添加CBitmapButton类型的数据成员。
第四步:映射按钮所在对话框类WM_INITDIALOG消息,在OnInitDialog函数中用刚添加的CBitmapButton类型的数据成员调用
AutoLoad函数把每个按钮和两个匹配的位图资源连接起来。如:VERIFY(CWnd::EnableWindow.AutoLoad(IDC_BUTTON1,this));
说明一:
CBitmapButton::AutoLoad
BOOL AutoLoad( UINT nID, CWnd* pParent );
//nID:The button’s control ID.
//pParent:Pointer to the object that owns the button.
//AutoLoad Associates a button in a dialog box with an object of the CBitmapButton class, loads the bitmap(s) by name, and sizes the button to fit the bitmap.(注意哦,这里的loads the bitmap by name。)
//Use the AutoLoad function to initialize an owner-draw button in a dialog box as a bitmap button.
说明二:
VERIFY
VERIFY( booleanExpression )
//VERIFY是MFC诊断宏,当参数booleanExpression 为 0 的时候,停止程序并显示一个对话框;当booleanExpression 不为零,则什么也不做。
说明三:
在为按钮设置的位图加名字标识的时候:
"按钮Caption+U"  表示按钮凸起状态的位图
"按钮Caption+D"  表示按钮凹起状态的位图
"按钮Caption+F"  表示按钮有焦点状态的位图
"按钮Caption+X"  表示按钮无效状态的位图

9,CWnd::EnableWindow函数可以是一个窗口有效或无效,可以调用它来使按钮等控件(窗口)无效或恢复有效。
CWnd::EnableWindow 
BOOL EnableWindow( BOOL bEnable = TRUE );
//If this parameter is TRUE, the window will be enabled. If this parameter is FALSE, the window will be disabled.
//EnableWindow Enables or disables mouse and keyboard input. When input is disabled, input such as mouse clicks and keystrokes is ignored. When input is enabled, the window processes all input.
//An application can use this function to enable or disable a control in a dialog box. A disabled control cannot receive the input focus, nor can a user access it.


/
///

 第十一章:Winsows消息处理和多线程编程


一)Winsows消息处理

1,单线程:程序代码只有一条执行路径。

2,单线程程序消息处理过程:
MSG message;
while(::GetMessage(&message,NULL,0,0))
{
::TranslateMessage(&message);//翻译如wm_char消息
::DispatchMessage(&message);//把消息分发给指定窗口的回调函数
}
说明:
1)os决定哪个消息属于我们的程序,当一个消息要处理的时候,用GetMessage函数返回该消息。
2)如果没有属于我们程序的消息发出,则我们的程序被挂起,而其它的程序可以运行;当属于我们程序的消息到达,我们的程序被唤醒

3,改造2中的单线程消息处理过程:
问题一:某个消息控制函数很“笨重”,消耗CPU时间过久。
问题二:DispatchMessage函数要等到该笨重消息控制函数返回后才能返回。在该笨重消息控制函数返回之前DispatchMessage不处理(分发)任何消息。
解决方法:使用PeekMessage函数使占用CPU时间过久的消息控制函数每隔一段时间交出控制一次。(在占CPU时间过久的控制函数中利用PostMessage发送
代码如下:
MSG message;
while(::PeekMessage(&message,NULL,0,0,PM_REMOVE))
{
::TranslateMessage(&message);//翻译如wm_char消息
::DispatchMessage(&message);//把消息分发给指定窗口的回调函数
}
说明:
//The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
简要说明:
1)BOOL PeekMessage(
  LPMSG lpMsg,         // pointer to structure for message
  HWND hWnd,           // handle to window
  UINT wMsgFilterMin,  // first message
  UINT wMsgFilterMax,  // last message
  UINT wRemoveMsg      // removal flags
);
//If a message is available, return nonzero,otherwise return 0;
//Unlike the GetMessage function, the PeekMessage function does not wait for a message to be placed in the queue before returning.
//PeekMessage retrieves only messages associated with the window identified by the hWnd parameter or any of its children as specified by the IsChild function, and within the range of message values given by the wMsgFilterMin and wMsgFilterMax parameters.
//If hWnd is NULL, PeekMessage retrieves messages for any window that belongs to the current thread making the call.
//If hWnd is –1, PeekMessage only returns messages with a hWnd value of NULL, as posted by the PostThreadMessage function.
//If wMsgFilterMin and wMsgFilterMax are both zero, PeekMessage returns all available messages (that is, no range filtering is performed).
//PeekMessage does not retrieve messages for windows that belong to other threads.

2)BOOL GetMessage(
  LPMSG lpMsg,         // address of structure with message
  HWND hWnd,           // handle of window
  UINT wMsgFilterMin,  // first message
  UINT wMsgFilterMax   // last message
);
//GetMessage does not retrieve messages for windows that belong to other threads or applications.
//

4,计时器独立于CPU时钟速度。
1)计时器的使用:用一个时间间隔参数调用CWnd::SetTimer函数,然后再用ClassWizard为WM_TIMER添加消息控制函数。
2)一旦调用CWnd::SetTimer启动了计时器,则WM_TIMER消息会被持续发送到指定的窗口,直至调用CWnd::KillTimer函数或计时器窗口被取消为止。
3)由于WINDOWS非实时OS,如果时间间隔少于100毫秒,计时器可能不准。
4)OS发送计时器消息的时候,如果队列里已经有了计时器消息,则不会把同样的计时器消息放进消息队列里。
说明:
1)CWnd::SetTimer
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD) );
//Return The timer identifier of the new timer if the function is successful. An application passes this value to the KillTimer member function to kill the timer. Nonzero if successful; otherwise 0.
//nIDEvent:Specifies a nonzero timer identifier.
//nElapse:Specifies the time-out value, in milliseconds.
//lpfnTimer:Specifies the address of the application-supplied TimerProc callback function that processes the WM_TIMER messages. If this parameter is NULL, the WM_TIMER messages are placed in the application’s message queue and handled by the CWnd object.
//CWnd::SetTimer Installs a system timer. A time-out value is specified, and every time a time-out occurs, the system posts aWM_TIMER message to the installing application’s message queue or passes the message to an application-defined TimerProc callback function.
2)CWnd::KillTimer
BOOL KillTimer( int nIDEvent );
//Return value:Return nozero if the event is killed.It is 0 if the KillTimer member function could not find the specified timer event.
//CWnd::KillTimer Kills the timer event identified by nIDEvent from the earlier call to SetTimer. Any pending WM_TIMER messages associated with the timer are removed from the message queue.


5,在引入多线程编程之前,WINDOWS程序员曾使用空状态处理来完成一些后台任务。(这里是OnIdle函数)
1)应用程序框架在框架消息处理循环中调用CWinApp::OnIdle(虚函数),可以重载它来处理后台任务。通常,一但OnIdle函数完成了它的工作,就要等到下次应用程序消息队列空了后才被调用。基类的OnIdle会更新工具栏按钮和状态栏指示器,并清除各种临时对象指针。
2)重载OnIdle函数可以用来更新用户界面,很有实际意义。(注:在重载OnIdle函数中一定要调用基类的OnIdle函数哦)
3)如果用户运行在一个模式对话框或正在使用菜单,则这时OnIdle函数不被调用。但我们可以在框架类中添加WM_ENTERIDLE消息控制函数来在上叙情况下使用后台处理。(注意,这里是在框架类中添加,不是在视图类中。弹出式对话框中是属于应用程序主框架窗口。)

///
///

二,多线程编程:

1,一些概念:
1)一个进程就是一个运行的程序,进程具有独立的内存、文件句柄和其它系统资源。
2)一个独立的进程可以包含多条执行路径,称为线程。线程有OS管理,每个线程有自己的堆栈。
3)大多数情况下,进程的所有代码和数据空间被进程内所有的线程所共享。
4)Windows提供两中线程:辅助线程 和 用户界面线程。mfc对两种线程都支持。
5)一个用户界面线程有窗口,因而有自己的消息循环;辅助线程没有窗口,因而不需要消息处理。

2,启动辅助线程:
准备工作:写一个全局函数。格式:UINT MyControllingFunction( LPVOID pParam );
启动辅助线程:使用AfxBeginThread函数为全局函数MyControllingFunction创建线程。
说明一:
MFC 通过参数重载提供两个版本的 AfxBeginThread:一个用于用户界面线程,另一个用于辅助线程。
//用户界面线程通常用于处理用户输入和响应用户事件,这些行为独立于执行该应用程序其他部分的线程。已经创建并启动主应用程序线程(在 CWinApp 导出的类中提供)。
//辅助线程通常用于处理后台任务,用户不必等待就可以继续使用应用程序。重新计算和后台打印等任务是很好的辅助线程示例。
说明二:
创建用户界面线程比创建辅助复杂(需要重写一些相关的函数),这里只笔记创建辅助线程一些笔记。
1)AfxBeginThread
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
//Return Value:Pointer to the newly created thread object.
//pfnThreadProc:Points to the controlling function for the worker thread. Cannot be NULL. This function must be declared as follows:    UINT MyControllingFunction( LPVOID pParam );
//pParam:Parameter to be passed to the controlling function as shown in the parameter to the function declaration in pfnThreadProc.
//nPriority:The desired priority(优先权) of the thread. If 0, the same priority as the creating thread will be used. For a full list and description of the available priorities, seeSetThreadPriority in the Win32 Programmer’s Reference.
//nStackSize:Specifies the size in bytes of the stack for the new thread. If 0, the stack size defaults to the same size stack as the creating thread.
//dwCreateFlags:Specifies an additional flag that controls the creation of the thread. This flag can contain one of two values:
[CREATE_SUSPENDED]   Start the thread with a suspend count of one. The thread will not execute until ResumeThread is called.
[ 0  ]  Start the thread immediately after creation.
//lpSecurityAttrs:Points to a SECURITY_ATTRIBUTES structure that specifies the security attributes for the thread. If NULL, the same security attributes as the creating thread will be used.
//Remarks:
Call this function to create a new thread. The first form of AfxBeginThread creates a worker thread. The second form creates a user-interface thread.
To end the thread, call AfxEndThread from within the thread, or return from the controlling function of the worker thread.
2)挂起线程:
CWinThread::SuspendThread
DWORD SuspendThread( );
//Return Value:The thread’s previous suspend count if successful; 0xFFFFFFFF otherwise.
//CWinThread::SuspendThread:Increments the current thread’s suspend(悬挂,延缓) count. If any thread has a suspend count above zero, that thread does not execute.
3)恢复线程运行:
CWinThread::ResumeThread
DWORD ResumeThread( );
//Return Value:The thread’s previous suspend count if successful; 0xFFFFFFFF otherwise. If the return value is zero, the current thread was not suspended. If the return value is one, the thread was suspended, but is now restarted. Any return value greater than one means the thread remains suspended.
//Remarks:Called to resume(恢复) execution of a thread that was suspended(暂停,延缓) by the SuspendThread member function, or a thread created with the CREATE_SUSPENDED flag. The suspend count of the current thread is reduced by one. If the suspend count is reduced to zero, the thread resumes execution; otherwise the thread remains suspended.
4)创建辅助线程事例:
UINT MyThreadProc( LPVOID pParam )
{
    CMyObject* pObject = (CMyObject*)pParam;
    if (pObject == NULL ||
        !pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))
    return 1;   // if pObject is not valid
    // do something with 'pObject'
    return 0;   // thread completed successfully
}
// inside a different function in the program
...
pNewObject = new CMyObject;
AfxBeginThread(MyThreadProc, pNewObject);

3,主线程 和 辅助线程 的通话:(这里的主线程指应用程序,是个用户界面线程)
1)最简单的方法:使用全局变量。
(注意:如书上事例B中在一个辅助线程中使用全局的计数器,不希望其它现在在计数器递增的时候由于其它线程访问而引起混乱,则将起声明成volatile变量保证计数器不被保存到寄存器中,也可以使用InterlockedIncrement来阻塞其它线程同时使计数器递增。)
补充一:
InterlockedIncrement
LONG InterlockedIncrement(
  LPLONG lpAddend   // pointer to the variable to increment
);
//The InterlockedIncrement function both increments (increases by one) the value of the specified 32-bit variable and checks the resulting value. The function prevents more than one thread from using the same variable simultaneously.
补充二:
volatile
使用 volatile 修饰符能够确保一个线程检索由另一线程写入的最新值。
//当字段声明中含有 volatile 修饰符时,该声明引入的字段为易失字段。

由于采用了优化技术(它会重新安排指令的执行顺序),在多线程的程序运行环境下,如果不采取同步控制手段,则对于非易失字段的访问可能会导致意外的和不可预见的结果。这些优化可以由编译器、运行时系统或硬件执行。但是,对于易失字段,优化时的这种重新排序必须遵循以下规则:

读取一个易失字段称为易失读取。易失读取具有“获取语义”;也就是说,按照指令序列,所有排在易失读取之后的对内存的引用,在执行时也一定排在它的后面。
写入一个易失字段称为易失写入。易失写入具有“释放语义”;也就是说,按照指令序列,所有排在易失写入之前的对内存的引用,在执行时也一定排在它的前面。
这些限制能确保所有线程都会观察到由其他任何线程所执行的易失写入(按照原来安排的顺序)。一个遵循本规范的实现并非必须做到:使易失写入的执行顺序,在所有正在执行的线程看来都是一样的。易失字段的类型必须是下列类型中的一种:
引用类型。

类型 byte、sbyte、short、ushort、int、uint、char、float 或 bool。
枚举基类型为 byte、sbyte、short、ushort、int 或 uint 的枚举类型。
(China msdn-C# 语言规范 )

2)不能使用Windows消息(即不能主线程向辅助线程发送消息通信),辅助线程没有窗口没有消息循环。


4,辅助线程 和 主线程(用户界面线程) 通信
1)辅助线程向主线程(用户界面线程)发送Windows消息,由主线程响应该消息,从而实现通信。(主线程有一个窗口,可见或不可见,如果辅助线程可以得到主线程的窗口句柄,便可以向主线程发送Windows消息了。主线程总是有一个消息循环的。)
2)辅助线程可以通过AfxBeginThread函数参数传入主线程句柄从而得到主线程的句柄。
3)辅助线程使用寄出(post)消息。任何用户定义的消息都可以。(使用送出(SEND)消息会引起主线程MFC消息处理代码的重入,这在模式对话框中会出现问题。)


5,EX11B事例说明:
1)CComputeDlg::OnStart函数中,利用AfxBeginThread(ComputeThreadProc, GetSafeHwnd(),THREAD_PRIORITY_NORMAL);函数为用户自定义的全局函数ComputeThreadProc创建辅助线程的同时,利用GetSafeHwnd()获得对话框句柄并做为参数传入
ComputeThreadProc函数形参pParam中。
//GetSafeHwnd() Returns the window handle for a window. Returns NULL if the CWnd is not attached to a window or if it is used with a NULL CWnd pointer.
2)UINT ComputeThreadProc(LPVOID pParam)函数中利用传进来的参数pParam,调用::PostMessage((HWND) pParam, WM_THREADFINISHED, 0, 0)函数向对话框窗口发送消息用户自定义WM_THREADFINISHED消息。
3)在对话框类中为WM_THREADFINISHED添加控制函数。
三步:
消息控制函数声明:CComputeDlg类头文件 LRESULT OnThreadFinished(WPARAM wParam, LPARAM lParam);消息映射:CComputeDlg类代码文件 ON_MESSAGE(WM_THREADFINISHED, OnThreadFinished)
消息控制函数:CComputeDlg类代码文件
LRESULT CComputeDlg::OnThreadFinished(WPARAM wParam, LPARAM lParam)
{
 CDialog::OnOK();
 return 0;
}

6,排斥区(CCriticalSection)
MFC提供了CCriticalSection类来帮助我们实现在线程之间共享全局数据(保证对其临界访问).
使用方法下面代码演示:
CCriticalSection g_cs;//定义g_cs为临界访问对象
int g_nCount;
voit func()
{
 g_cs.Lock();
 g_nCount++;
 g_cs.Unlock();
}
说明:
1)CCriticalSection从CSyncObject类派生而来:
An object of class, CCriticalSection represents a "critical section" - a synchronization object that allows one thread at a time to access a resource or section of code. Critical sections are useful when only one thread at a time can be allowed to modify data or some other controlled resource.
2)构造函数CCriticalSection( )说明:
Constructs a CCriticalSection object. To access or release a CCriticalSection object, create a CSingleLock object and call its Lock and Unlock member functions. If the CCriticalSection object is being used stand-alone, call its Unlock member function to release it.
3)CCriticalSection::Unlock:  Releases the CCriticalSection object.
CCriticalSection::Lock: Use to gain access to the CCriticalSection object.
4)进一步使用说明:
当线程A正在执行func()函数使g_nCount++增1的时候,线程B调用func()函数执行到g_cs.Lock()的时候线程B被阻塞直到线程A执行了g_cs.Unlock()才继续往下执行g_nCount++。
5)CCriticalSection只是用于当个进程内的控制访问,如果要在不同的进程之间控制数据的访问需要使用 互斥体(CMutex) 和 信号(semaphore)。


7,互斥体(CMutex)
1)CMutex:
An object of class CMutex represents a “mutex” — a synchronization object that allows one thread mutually exclusive access to a resource. Mutexes are useful when only one thread at a time can be allowed to modify data or some other controlled resource.
2)CMutex类从CSyncObject类派生而来,其一般用法参见下E文:
To use a CMutex object, construct the CMutex object when it is needed. Specify the name of the mutex you wish to wait on, and that your application should initially own it. You can then access the mutex when the constructor returns. Call CSyncObject::Unlock when you are done accessing the controlled resource.


8,信号(CSemaphore)
1)CSemaphore(也是从CSyncObject类派生而来):
An object of class CSemaphore represents a “semaphore” — a synchronization object that allows a limited number of threads in one or more processes to access a resource. A CSemaphore object maintains a count of the number of threads currently accessing a specified resource.
2)Semaphores are useful in controlling access to a shared resource that can only support a limited number of users.The current count of the CSemaphore object is the number of additional users allowed. When the count reaches zero, all attempts to use the resource controlled by the CSemaphore object will be inserted into a system queue and wait until they either time out or the count rises above 0. The maximum number of users who can access the controlled resource at one time is specified during construction of the CSemaphore object.
构造函数:
CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL );

9,关于从CSyncObject类派生类一些说明:
1)CCriticalSection,CMutex,CSemaphore,CEvent,derived from CSyncObject。
2)CSyncObject类成员中包含一下两成员函数:
Lock   Gains access to the synchronization object.
Unlock   Releases access to the synchronization object.



笔记后语:多线程编程是个大块的内容。这些书上只略概了些,笔记作用不是很大,详见MSDN和专门指导书更有实践性。日后等俺功力到火候了,一定做些专题性的总结笔记。

 


//

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值