win32API多线程编程

原文1:https://blog.csdn.net/gengshenghong/article/details/6945216

原文2:https://www.cnblogs.com/Tony100K/p/11758936.html

Ref:

MSDN: http://msdn.microsoft.com/zh-cn/library/y6h8hye8(v=VS.100)

一、win32多线程的创建

Win32多线程的创建方法主要有:

(1)CreateThread()

(2)_beginthread()&&_beginthreadex()

(3)AfxBeginThread()

(4)CWinThread类

1、CreateThread()

百度百科:http://baike.baidu.com/view/1191444.htm

函数原型:

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  DWORD dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId);
}

头文件:Windows.h

CreateThread是Win32提供的创建线程的最基础的API,用于在主线程上创建一个线程。返回一个HANDLE句柄(内核对象)。

参数简要说明:

lpThreadAttributes:线程属性,用于设置线程的属性,NULL表示使用默认的设置。dwStackSize:线程堆栈大小,使用0采用默认设置,windows会根据需要动态增加堆栈大小。lpStartAddress:指向线程函数的指针。lpParameter:向线程函数传递的参数。dwCreationFlags:线程标志,CREATE_SUSPENDED表示创建一个挂起的线程,0表示创建后立即激活线程。lpThreadId,先线程的ID(输出参数)。

创建线程的代码:

#include "stdafx.h"
#include <Windows.h>
 
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	printf("sub thread started\n");
	printf("sub thread finished\n");
	return 0;
}
 
int main(int argc, char* argv[])
{
	DWORD threadID;
	HANDLE hThread;
	hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID);	// 创建线程
 
	return 0;
}

如果直接使用上面的代码,那么很可能没有任何输出,这是由于主线程创建了子线程后主线程继续向下运行,子线程还没来得及执行里面的代码主线程可能就结束了。这就需要另一个API来进行同步:WaitForSingleObject()。

与之对应的还有WaitForMultipleObjects,用于同步一组内核对象。(参考http://msdn.microsoft.com/zh-cn/site/ms686360获取所有的同步函数(Synchronization Functions)的使用。

WaitForSingleObject原型:DWORD WINAPI WaitForSingleObject(__in HANDLE hHandle, __in DWORD dwMilliseconds);其中,第一个参数是要等待的内核对象的句柄,第二个参数是设置等待超时时间,可以设置为INFINITE,表示一直等待直到有信号触发。

在内核对象使用完毕后,一般需要关闭,使用CloseHandle()函数,参数为内核对象句柄。

所以,以下是一个最基本的使用CreateThread的例子:

#include "stdafx.h"
#include <Windows.h>
 
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	printf("sub thread started\n");
	// TODO: Add your thread code here.
	printf("sub thread finished\n");
	return 0;
}
 
int main(int argc, char* argv[])
{
	DWORD threadID;
	HANDLE hThread;
	hThread = CreateThread(NULL,0,ThreadProc,NULL,0,&threadID);	// 创建线程
	
	WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hThread);	// 关闭内核对象
	
	return 0;
}

2、_beginthread()&&_beginthreadex()

百度百科:http://baike.baidu.com/view/3029167.htm

MSDN:http://msdn.microsoft.com/zh-cn/library/kdzttdcb.aspx

函数原型:

uintptr_t _beginthread( // NATIVE CODE
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

头文件:process.h

参数说明:第一个参数是线程函数的指针,第二个参数是堆栈大小,第三个参数是要传递给线程函数的参数列表。返回值也是线程句柄(关于更多说明,参考MSDN)。
同样,对于_beginthread()的同步,和CreateThread一样可以使用WaitForSingleObject函数,CloseHandle()关闭内核对象。另外,_beginthread()的线程函数是无返回值类型的,可以使用_endthread()在线程函数中结束线程。

下面是一个使用_beginthread()的基本的例子:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
 
void __cdecl ThreadProc(void *para)
{
    printf("sub thread started\n");
	// TODO: Add your thread code here.
	printf("sub thread finished\n");
	_endthread();	// 可以省略,隐含会调用。
}
 
int main(int argc, char* argv[])
{
	HANDLE hThread = (HANDLE)_beginthread(ThreadProc, 0, NULL);
 
	WaitForSingleObject(hThread,INFINITE);
	CloseHandle(hThread);
}

另外,还有一个函数_beginthreadex(),可以简单的认为_beginthread()为其简化版,所以更多的时候是使用更简单的_beginthread()了。
说明:在MSDN中可以看到一句很重要的提示,内容为“For an executable file linked with Libcmt.lib, do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread.”,简单翻译就是说,对于链接Libcmt.lib的可执行程序,不要使用Win32的线程退出函数(ExitThread),这会阻止运行时系统回收分配的资源,应该使用_endthread,它能回收分配的线程资源然后调用ExitThread。这个问题看似没有提到CreateThread(),但是其实有关,这就是经常看到有些资料上坚决的说到”不要使用CreateThread创建线程,否则会内存泄漏“的来源了。
问题引出:CreateThread的内存泄漏问题(CreateThread和_beginthread的区别)

Related Topics:http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html http://www.cnblogs.com/whiteyun/archive/2011/06/02/2067742.html ....

(1) _beginthread也是通过CreateThread来创建线程的,只是_beginthread对其进行了一些封装,将相关”资源“通过线程的本地存储(TLS)传递给了线程函数的参数,然后在调用_endthread的时候,会将这些保存的资源进行释放。

(2) 并不是所有的使用CreateThread的情况都会有内存泄漏。看了很多人的文章,只有http://wenku.baidu.com/view/adede4ec4afe04a1b071dea4.html的分析是最清晰的,我已经转到http://dl.dbank.com/c03ljl2iud了,可下载查看(版权归原作者所有)。

总之,建议是使用_beginthread取代CreateThread来创建线程。

3、AfxBeginThread():

很显然,这是MFC中的Afx系列函数,一个在MFC中创建线程的全局函数。由于现在也不怎么用MFC了,这里就不多说了。

4、CWinThread类:

很显然,是MFC中创建线程的类,同上,不多说了。

5、补充内容

关于WaitForMultipleObjects在_beginthread无法使用的问题

问题:使用_beginthread创建多个线程,无法使用WaitForMultipleObjects来进行同步。

这个问题可以用下面的例子来测试:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
 
void __cdecl ThreadProc(void *para)
{
	printf("sub thread started\n");
	// TODO: Add your thread code here.
	printf("sub thread finished\n");
	_endthread();	// 可以省略,隐含会调用。
}
 
int main(int argc, char* argv[])
{
	DWORD threadID;
	HANDLE hThread[10];
	for(int i =0;i<10;i++)
		hThread[i] = (HANDLE)_beginthread(ThreadProc,0,NULL);
 
	WaitForMultipleObjects(10, hThread,TRUE,INFINITE);		//无法同步所有线程!
	for(int i = 0;i<10;i++) {
		CloseHandle(hThread);
	}
}

期望的结果是程序能输出10次的线程创建结束的消息,但是实际运行发现,无法达到这么多次数。为何?这是因为WaitForMultipleObjects在这里无法正常工作。原因是:
_endthread()在结束线程的时候,会自动调用CloseHandle关闭内核对象。这就容易解释了,如果提前关闭了内核对象,WaitForMultipleObjects会返回错误。那么有没有专门用于_beginthread创建的线程的同步方法呢,就我目前所知,好象是没有的!要解决这里的问题,可以分别调用WaitForSingleObject来同步,当然,使用其他一些变相的方法也是可以的,另外,上面的CloseHandle当然就可以不用再调用了。

总结:看来_beginthread也不是那么好用。:)

二、创建执行挂起终止

  • CreateThread(NULL, 0, FunOne, (void*)&input, CREATE_SUSPENDED, NULL);
  • 安全属性 栈大小 线程函数 参数指针 附加标志 ID号(32位int指针)
  • 附加标志为0 创建即可执行
  • 附加标志为CREATE_SUSPENDED 创建就要挂起
  • ResumeThread(句柄) 挂起计数器-1,为0则进行
  • SuspendThread(句柄) 挂起计数器+1
  • TerminateThread(hand1, 1) 第二个参数为exitcode
  • 调用SetThreadPriority函数设置线程的相对优先级,例如
    Bool SetThreadPriority (HANDLE hPriority , int nPriority)
    参数hPriority 指向待设置的线程句柄
  • nPriority 是线程的相对优先级,可以是以下的值:
    空闲:THREAD - PRIORITY- IDLE 15
    最低线程:THREAD - PRIORITY- LOWEST 2
    低于正常线程:THREAD - PRIORITY- BELOW- NORMAL 1
    正常线程:THREAD - PRIORITY- NORMAL 0
    高于正常线程:THREAD - PRIORITY- ABOVE – NORMAL -1
    最高线程:THREAD - PRIORITY- HIGHEST -2
    关键时间:THREAD - PRIORITY- TIME – CRITICAL -15
  • 挂起与恢复函数原型
    DWORD SuspendThread(HANDLE hThread);
    挂起指定的线程(慎用,不处理同步对象)
    如果函数执行成功,则线程的执行被终止
    每次调用SuspendThread() 函数,线程将挂起计数器的值增1

    DWORD ResumeThread(HANDLE hThread);
    结束线程的挂起状态来执行这个线程
    每次调用ResumeThread() 函数,线程将挂起计数器的值减1
    若挂起计数器的值为0,则不会再减

#include "stdio.h"
#include <windows.h> 
#include <iostream> 
using namespace std;

//简单的多线程创建、执行、挂起、终止的程序例子。

DWORD WINAPI FunOne(LPVOID param) {
    while (true)
    {
        Sleep(1000);
        cout << "hello! ";
        //cout<<"hello! "<<*((int*)param); 
    }
    return 0;
}
DWORD WINAPI FunTwo(LPVOID param) {
    while (true)
    {
        Sleep(1000);
        cout << "world! ";
    }
    return 0;
}

//注意创建线程函数的第五个参数的运用。
//输入1和2数字,可以控制线程的启动和终止。
//注意连续输入1和2数字线程的运行情况。
//线程的终止函数。

int main(int argc, char* argv[])
{
    int input = 0;

    //创建线程。这里第四个参数值设为NULL也可以,因为线程函数里没有使用输入参数。
    HANDLE hand1 = CreateThread(NULL, 0, FunOne, (void*)&input, CREATE_SUSPENDED, NULL);
    HANDLE hand2 = CreateThread(NULL, 0, FunTwo, (void*)&input, CREATE_SUSPENDED, NULL);
    while (true) {
        cin >> input;
        if (input == 1)
        {
            //恢复线程
            ResumeThread(hand1);
            ResumeThread(hand2);
        }
        if (input == 2)
        {
            //挂起线程
            SuspendThread(hand1);
            SuspendThread(hand2);
        }
        if (input == 0)
        {
            //终止线程
            TerminateThread(hand1, 1);
            TerminateThread(hand2, 1);
        }
        if (input == 9)
            return 0;
    };

    return 0;

}

三、利用全局变量实现同步

这样做可能会有一个问题,主线程结束时其他线程也就跟着结束了

  • 全局变量
    进程中的所有线程均可以访问所有的全局变量,因而全局变量成为Win32多线程通信的最简单方式。
    int var; //全局变量
    UINT ThreadFunction ( LPVOID pParam {
    while (var)
    {
    …… //线程处理
    }
    return 0; }
    var是一个全局变量,任何线程均可以访问和修改。线程间可以利用此特性达到线程同步的目的。
#include "stdio.h"
#include <windows.h> 
#include <iostream> 
using namespace std;

//使用全局变量同步线程的例子。

//全局变量。
int globalvar = false;

DWORD WINAPI ThreadFunc(LPVOID pParam)
{
    cout << "  ThreadFunc  " << endl;
    Sleep(100);

    //修改全局变量的值。
    globalvar = true;

    return 0;
}
DWORD WINAPI ThreadFunc1(LPVOID pParam)
{
    while (1)
        cout << "  ThreadFunc111  " << endl;
    return 0;
}
DWORD WINAPI ThreadFunc2(LPVOID pParam)
{
    while (1)
        cout << "  ThreadFunc222  " << endl;
    return 0;
}

//这种方式可能存在一些问题。

int main()
{
    HANDLE hthread1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);
    HANDLE hthread2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);
    HANDLE hthread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
    if (!hthread)
    {
        cout << "Thread Create Error ! " << endl;
        CloseHandle(hthread);
    }
    bool b =SetThreadPriority(hthread,15);
    cout<<GetThreadPriority(hthread1)<<endl;
    cout<<GetThreadPriority(hthread2)<<endl;
    cout<<GetThreadPriority(hthread)<<endl;

    //循环判断全局变量的值。
    while (!globalvar)
        cout << "Thread while" << endl;

    cout << "Thread exit" << endl;
    return 0;
}

四、利用事件实现同步

  • 事件是WIN32提供的最灵活的线程间同步方式。
    事件存在两种状态:
    激发状态(signaled or true)
    未激发状态(unsignal or false)
    事件可分为两类:
    手动设置:这种对象只能用程序来手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。
    SetEvent只有一个参数,该参数指定了事件对象的句柄值,若事件成功激发,返回TRUE;
    ResetEvent函数将事件对象恢复到最初的非激发状态,只有一个参数,成功后返回真
    自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。

  • CreateEvent(NULL, FALSE, FALSE, NULL);
  • 第一个参数还是安全,默认为NULL,二参代表事件类型,true为手动清除信号,false为自动清除
  • 三参:事件的初始状态 四参:事件的名称
  • setEvent(句柄)
  • WaitForSingleObject(evRead, INFINITE);等待某个事件
    WaitForSingleObject (evFinish, INFINITE)
    参数1:等待对象
    参数2:等待时间
    返回值:WAIT_OBJECT_0(激发)
    WAIT_TIMEOUT(超时)
    WAIT_FAILED(错误)
  • WaitForMultipleObjects(2 ,evFin ,TRUE ,INFINITE)
    参数1:等待的句柄数
    参数2:等待的句柄数组
    参数3:确定是否等待所有句柄激发后才返回
    参数4:等待时间
    返回值: WAIT_OBJECT_I(激发事件在句柄数组中的索引)
  • 例子:有三个线程
    主线程、读线程ReadThread、写线程WriteThread
    读线程ReadThread必须在写线程WriteThread 的写操作完成之后才能进行读操作
    主线程必须在读线程ReadThread 的读操作完成后才结束
    定义两个事件对象evRead,evFinish
    evRead由写线程WriteThread用于通知读线程ReadThread 进行读操作
    evFinish由读线程ReadThread用于通知主线程读操作已经结束

#include "stdio.h"
#include <windows.h> 
#include <iostream> 
using namespace std;

//**使用事件机制同步线程的例子。

//两个事件。
HANDLE evRead, evFin;
HANDLE evWrite;

void ReadThread(LPVOID param)
{
    //等待读事件。
    WaitForSingleObject(evRead, INFINITE);

    cout << "Reading" << endl;
    //ResetEvent(evRead);//evRead为手动恢复类型时打开
    SetEvent(evWrite);
    WaitForSingleObject(evRead, INFINITE);
    cout << "Reading11111" << endl;

    //激活结束事件。
    SetEvent(evFin);
}
void WriteThread(LPVOID param)
{
    cout << "Writing" << endl;

    //激活读事件。
    SetEvent(evRead);

    WaitForSingleObject(evWrite, INFINITE);
    cout << "Writing11111" << endl;
    SetEvent(evRead);
}
int main(int argc, char* argv[])
{
    //创建两个事件,注意事件参数的含义。
    evRead = CreateEvent(NULL, FALSE, FALSE, NULL);
    evFin = CreateEvent(NULL, FALSE, FALSE, NULL);

    evWrite = CreateEvent(NULL, FALSE, FALSE, NULL);
    //evRead = CreateEvent (NULL ,TRUE ,FALSE ,NULL) ;//修改第二个参数为TRUE
    //evFin = CreateEvent (NULL ,TRUE ,FALSE ,NULL) ;//修改第二个参数为TRUE
    //evWrite = CreateEvent (NULL ,TRUE ,FALSE ,NULL) ;//修改第二个参数为TRUE

    _beginthread(ReadThread, 0, NULL);
    _beginthread(WriteThread, 0, NULL);

    //等待结束事件。
    WaitForSingleObject(evFin, INFINITE);

    cout << "The Program is End" << endl;
    return 0;
}

五、临界区

  • 防止多个线程同时执行一个特定代码段的机制
    适用于多个线程操作之间没有先后顺序,但要求互斥的同步
    多个线程访问同一个临界区的原则:
    一次最多只能一个线程停留在临界区内
    不能让一个线程无限地停留在临界区内,否则其它线程将不能进入该临界区
    定义临界区变量的方法如下:
    CRITICAL_SECTION gCriticalSection;
    通常情况下,CRITICAL_SECTION结构体应该被定义为全局变量,便于进程中的所有线程可以方便地按照变量名来引用该结构体。
  • 初始化临界区
    VOID WINAPI InitializeCriticalSection (
    LPCRITICAL_SECTION lpCriticalSection );
    删除临界区
    VOID WINAPI DeleteCriticalSection (
    LPCRITICAL_SECTION lpCriticalSection );
    进入临界区
    VOID WINAPI EnterCriticalSection (
    LPCRITICAL_SECTION lpCriticalSection );
    执行该语句时,程序会判断cs对象是否已被锁定,若没锁定则线程进入临界区,并将cs置为锁定状态;否则,线程线程被阻塞以等待cs解锁。
    离开临界区
    VOID WINAPI LeaveCriticalSection (
      LPCRITICAL_SECTION lpCriticalSection );
    线程执行该语句后,程序自动将cs解锁,并唤醒等待cs解锁的线程
  • 使用临界区编程的一般方法是:
    void WriteData()
    {
    EnterCriticalSection(&gCriticalSection);
    //do something
    LeaveCriticalSection(&gCriticalSection);
    }
    例子
    假如一个银行系统有两个线程执行取款任务,一个使用存折在柜台取款,一个使用银行卡在ATM取款。若不加控制,很可能账户余额不足两次取款的总额,但还可以把钱取走。
#include "stdio.h"
#include <windows.h> 
#include <iostream> 
#include <process.h>
#include <iostream>
#include <fstream>
using namespace std;

//**使用临界区机制同步线程。

int total = 100;
HANDLE evFin[2];
CRITICAL_SECTION cs;//临界区。

//可以去掉临界区机制,查看是否出现错误。(需要辅助sleep函数)
void WithdrawThread1(LPVOID param)
{
    EnterCriticalSection(&cs);//进入临界区
    if (total - 90 >= 0)
    {//Sleep(100);
        total -= 90;
        cout << "You withdraw 90" << endl;
    }
    else
        cout << "You do not have that much money" << endl;
    LeaveCriticalSection(&cs);//退出临界区
    SetEvent(evFin[0]);
}
void WithdrawThread2(LPVOID param)
{
    EnterCriticalSection(&cs);//进入临界区
    if (total - 20 >= 0)
    {
        total -= 20;
        cout << "You withdraw 20" << endl;
    }
    else
        cout << "You do not have that much money" << endl;
    LeaveCriticalSection(&cs);//退出临界区
    //LeaveCriticalSection(&cs) ;
    SetEvent(evFin[1]);
}
int main(int argc, char* argv[])
{
    evFin[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
    evFin[1] = CreateEvent(NULL, FALSE, FALSE, NULL);

    InitializeCriticalSection(&cs);//初始化临界区。

    _beginthread(WithdrawThread1, 0, NULL);//创建线程顺序影响执行的结果。
    _beginthread(WithdrawThread2, 0, NULL);
    //_beginthread(WithdrawThread1 , 0 , NULL) ;

    int id = WaitForMultipleObjects(2, evFin, TRUE, INFINITE);//等待两个事件都激活。
    cout << "句柄数组中的索引:" << id << endl;

    DeleteCriticalSection(&cs);//删除临界区

    cout << total << endl;
    return 0;
}

六、互斥量

  • 通常用于协调多个线程或进程的活动,通过对资源“锁定”和“取消锁定”,来控制对共享资源的访问。
    当一个互斥量被一个线程锁定了,其他试图对其加锁的线程就会被阻塞;当对互斥量加锁的线程解除锁定后,则被阻塞的线程中的一个会得到互斥量。
    互斥量的作用是保证每次只能有一个线程获得互斥量
    使用CreateMutex函数创建:
    HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,
    BOOL bInitialOwner,
    LPCTSTR lpName
    );
    安全属性 是否手动清除(true为手动) 名称
  • 相关的API:
    CreateMutex 创建一个互斥对象,返回对象句柄;
    OpenMutex 打开并返回一个已存在的互斥对象的句柄,使之后续访问;
    ReleaseMutex 释放对互斥对象的占用,使之成为可用;
    使用互斥量的一般方法是:
    void Writedata()
    {
    WaitForSingleObject(hMutex,…);
    ...//do something
    ReleaseMutex(hMutex);
    }
#include "stdio.h"
#include <windows.h> 
#include <iostream> 
#include <process.h>
#include <iostream>
#include <fstream>
using namespace std;

//**互斥量的使用方法。

#define THREAD_INSTANCE_NUMBER  3

LONG g_fResourceInUse = FALSE;
LONG g_lCounter = 0;

DWORD ThreadProc(void* pData) {

    int ThreadNumberTemp = (*(int*)pData);
    HANDLE hMutex;

    if ((hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "Mutex.Test")) == NULL) {
        cout << "Open Mutex error!" << endl;
    }

    //cout << "ThreadProc is running hMutexxxxxxx!!"  << ThreadNumberTemp <<endl;
    WaitForSingleObject(hMutex, INFINITE);//获取互斥量。
    cout << "ThreadProc is running!!" << ThreadNumberTemp << endl;
    cout << "ThreadProc gets the mutex-" << ThreadNumberTemp << endl;

    ReleaseMutex(hMutex);//释放互斥量。
    CloseHandle(hMutex);
    return 0;
}

int main(int argc, char* argv[])
{

    int i;
    DWORD ID[THREAD_INSTANCE_NUMBER];
    HANDLE h[THREAD_INSTANCE_NUMBER];
    HANDLE hMutex;
    if ((hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "Mutex.Test")) == NULL) {
        if ((hMutex = CreateMutex(NULL, FALSE, "Mutex.Test")) == NULL) //注意第二个参数,当前线程是否拥有创建的锁
        {
            cout << "Create Mutex error!" << endl;
            return 0;
        }
    }
    //ReleaseMutex(hMutex);  //CreateMutex函数第二参数为TRUE时打开

    //获取信号量的位置不同,将产生不同的结果。
   //WaitForSingleObject(hMutex,INFINITE);
    for (i = 0; i < THREAD_INSTANCE_NUMBER; i++)
    {
        WaitForSingleObject(hMutex, INFINITE);//获取互斥量。本线程可重复获取,但解锁时需释放相同的次数。
        h[i] = CreateThread(NULL,
            0,
            (LPTHREAD_START_ROUTINE)ThreadProc,
            (void*)&ID[i],
            0,
            &(ID[i]));
        //WaitForSingleObject(hMutex,INFINITE);//演示重复获取互斥量
        if (h[i] == NULL)
            cout << "CreateThread error" << ID[i] << endl;
        else
            cout << "CreateThread: " << ID[i] << endl;
        ReleaseMutex(hMutex);
        //Sleep(1000);
    }
    //ReleaseMutex(hMutex);//ReleaseMutex(hMutex);ReleaseMutex(hMutex);//演示释放重复获取的互斥量

    WaitForMultipleObjects(THREAD_INSTANCE_NUMBER, h, TRUE, INFINITE);
    cout << "Close the Mutex Handle! " << endl;
    CloseHandle(hMutex);

    return 0;
}

七、信号量

  • 信号量是一个核心对象,拥有一个计数器,可用来管理大量有限的系统资源
    当计数值大于零时,信号量为有信号状态
    当计数值为零时,信号量处于无信号状态
    创建信号量
    HANDLE CreateSemaphore (PSECURITY_ATTRIBUTE psa,
    LONG lInitialCount, LONG lMaximumCount, PCTSTR pszName);
    安全属性 初始数量 最大数量 信号量名称
  • 释放信号量
    BOOL WINAPI ReleaseSemaphore(HANDLE hSemaphore,
    LONG lReleaseCount, //信号量的当前资源数增加lReleaseCount
    LPLONG lpPreviousCount);
    打开信号量
    HANDLE OpenSemaphore (DWORD fdwAccess,
    BOOL bInherithandle, PCTSTR pszName );
// exa7.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h> 
#include <iostream>
using namespace std;

//**使用信号量机制同步线程。

#define THREAD_INSTANCE_NUMBER  3

DWORD foo(void * pData) {

    int ThreadNumberTemp = (*(int*) pData);
    HANDLE hSemaphore;

    if ((hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "Semaphore.Test")) == NULL) {
        cout << "Open Semaphore error!" << endl;
    }

    WaitForSingleObject(hSemaphore,INFINITE);//获取信号量。

    cout << "foo is running!!!"  << ThreadNumberTemp << endl;
    cout << "foo gets the semaphore-" << ThreadNumberTemp<< endl;
    
    ReleaseSemaphore(hSemaphore, 1, NULL);//释放一个单位的信号量

    CloseHandle(hSemaphore);
    return 0;
}

int main(int argc, char* argv[])
{

    int i;
    DWORD ThreadID[THREAD_INSTANCE_NUMBER]; 
    HANDLE hThread[THREAD_INSTANCE_NUMBER];
    HANDLE hSemaphore;

    
    if ((hSemaphore = CreateSemaphore(NULL,1,1, "Semaphore.Test")) == NULL ) {
        cout << "Create Semaphore error!" << endl;
        return 0;
    }   

    //与互斥量一样,这里获取信号量的位置不同,会产生不同的结果。
    for (i=0;i<THREAD_INSTANCE_NUMBER;i++)
    {
        WaitForSingleObject(hSemaphore,INFINITE);//获取信号量。不可重入。
        hThread[i] = CreateThread(NULL, 
                            0,                          
                            (LPTHREAD_START_ROUTINE) foo, 
                            (void *)&ThreadID[i],                    
                            0,                       
                            &(ThreadID[i]));              
        
        if (hThread[i] == NULL)

            cout << "CreateThread error" << ThreadID[i] << endl;
        else
            cout << "CreateThread: " << ThreadID[i] << endl;

        ReleaseSemaphore(hSemaphore, 1, NULL);
    }

    WaitForMultipleObjects(THREAD_INSTANCE_NUMBER,hThread,TRUE,INFINITE);
    cout << "Close the Semaphore Handle! " << endl;
    CloseHandle(hSemaphore);

    return 0;
}

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "精通Win32 API编程"是一本讲解Windows操作系统下API编程的书籍。该书的主要内容围绕Win32 API以及如何使用API来开发Windows应用程序展开。 Win32 API是一套应用程序接口,它提供了访问Windows操作系统的功能和资源的一系列函数和服务。精通Win32 API编程的PDF为读者提供了全面而深入的知识和技巧,使他们能够充分利用Win32 API来创建强大、高效、功能丰富的Windows应用程序。 该书的内容包括Win32 API的核心组件、常用编程概念和技术、消息处理、图形界面设计、内存管理、多线程编程、网络编程等。不仅介绍了基础知识,还着重讲解了高级和复杂的编程技术与技巧。 读者通过学习该书可以掌握使用Win32 API进行窗口和控件的创建、消息的处理、内存的管理、图形的绘制,以及文件和网络的操作等重要内容。此外,书中还介绍了调试和优化Windows应用程序的方法,帮助读者解决日常开发过程中遇到的各种问题。 "精通Win32 API编程"的PDF不仅对想要深入了解Windows编程的开发者有很大帮助,也适合计算机科学相关专业的学生学习。通过阅读该书,读者能够提升Windows应用程序开发的技能和水平,实现更高效、更稳定、更灵活的应用程序。 ### 回答2: 精通Win32 API编程是一本覆盖广泛的书籍,它涵盖了使用Win32 API进行Windows操作系统编程的方方面面。该书的主要目标是帮助读者深入理解Win32 API的用法和原理,并掌握在Windows平台上开发应用程序的技巧。 在这本书中,读者将学习如何使用Win32 API来创建窗口、处理窗口消息、绘制图形、处理文件I/O操作、实现多线程等。此外,该书还详细介绍了常见的Win32 API函数和结构体,以及它们的用法和参数。 这本书的特色之一是它深入探讨了Win32 API的内部工作原理。它解释了消息循环、窗口过程、消息处理机制等概念,并提供了大量的代码示例和实践案例,帮助读者更好地理解和应用这些概念。 此外,这本书还介绍了一些高级的Win32 API编程技巧,比如使用API Hooking进行函数的拦截和修改,通过定时器实现定时任务,以及使用消息队列实现进程间通信等。这些技巧可以帮助读者更好地定制和优化Windows应用程序。 总的来说,精通Win32 API编程是一本非常全面和深入的书籍,它适合那些已经具备一定编程基础的读者,并且想要深入学习和掌握Win32 API编程技术的人。通过学习这本书,读者可以充分利用Win32 API的强大功能,开发出高效、功能丰富的Windows应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值