内核对象的线程同步

1、 用户方式的线程同步与内核对象的线程同步:

用户方式:速度快是其优点,但是也有局限性。如互锁函数只能在单值上

运行,无法使线程进入等待状态。虽然可以使用关键代码段使线程进入等待状态,但是这样又会容易陷入死锁,因为在等待进入关键代码段时无法设定超时值。

内核对象方式:唯一的缺点就是速度慢,调用成本比较高。

对于线程同步来说,内核对象中的每种对象都可以说处于已通知或者未通知状态中。线程在运行时,处于未通知状态,线程终止运行时,处于已通知状态

2、 等待函数:

可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止。

HANDLE h[3];

h[0] = hProcess1;

h[1] = hProcess2;

h[2] = hProcess3;

DWORD dw = WaitForMultipleObjects(3,h,FALSE,5000);//第三个参数为FALSE说明只要有一个线程返回已通知就会触发,否则必须三个线程都返回

switch(dw)

{

case WAIT_OBJECT_0 + 0:

       //第一个线程终止运行了

       break;

case WAIT_OBJECT_0 + 1:

       //第二个线程终止运行了

       break;

case WAIT_OBJECT_0 + 2:

       //第三个线程终止运行了

       break;

case WAIT_TIMEOUT:

       //没有一个线程终止运行,超时了

       break;

case WAIT_FAILED:

       //返回错误值了

       break;

 

1)DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds);如果第二个参数为INFINITE,说明线程愿意一直等到下去,直到该线程终止运行。

代码举例如下:

DWORD dw = WaitForSingleObject(hProcess,5000);

switch(dw)

{

case WAIT_OBJECT_0:

       //等待的线程状态变为已通知,可以进行操作了

       break;

case WAIT_TIMEOUT:

       //等待超时

       break;

case WAIT_FAILED:

       //错误的返回值

       break;

}

 

2)WaitForMultipleObjects

不同之处是在于可以等待多个线程,实例代码如下:

3、事件内核对象:

事件内核对象,包含一个使用计数,一个用于指明该事件是自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是处于未通知状态的布尔值。

人工重置与自动重置的区别在于:人工重置的事件得到通知时,等待该事件的所有线程均变为可调度的线程;相反,自动重置事件得到通知,等待该事件的线程中只有一个线程变为可调度线程。

如果为自动重置事件,当线程成功的等待到该对象时,自动设置的事件就会自动重置到未通知状态,所以通常没有必要为自动重置事件调用ResetEvent函数

4、Handshake示例应用程序:

1)OnInitDialog()函数中

         //输出Edit对话框变为只读

       CEdit *pEditResult = (CEdit *)GetDlgItem(IDC_EDIT_RESULT);

       pEditResult->SetReadOnly();

 

       //创建两个事件,分别用于显示客户端、服务器是否处理完

       g_heventRequest = CreateEvent(NULL,FALSE,FALSE,NULL);

       g_heventResult= CreateEvent(NULL,FALSE,FALSE,NULL);

 

       //创建一个服务器线程

       DWORD dwThreadId;

       hThreadServer = CreateThread(NULL,0,ServerThread,NULL,NULL,&dwThreadId);

 

2)服务器线程

DWORD WINAPI ServerThread(PVOID pvParam)

{

       BOOL fShutdown = FALSE;

 

       while (!fShutdown)

       {

              //等待客户端进程停止

              WaitForSingleObject(g_heventRequest,INFINITE);

 

              fShutdown = (0 == wcscmp(g_szServerShutdown,g_szSharedRequestAndResultBuffer));

              //如果不是shutdown就反转

              if (!fShutdown)

              {

                     //反转

                     _tcsrev(g_szSharedRequestAndResultBuffer);

              }

 

              //让客户端可以调用

              SetEvent(g_heventResult);

       }

       return 0;

}

3)按钮的处理

 

void CHandShakeDlg::OnBnClickedSubmit()

{

       // 取出对话框中的内容

       CString csTemp;

       GetDlgItemTextW(IDC_EDIT_REQUEST,csTemp);

       _stprintf(g_szSharedRequestAndResultBuffer,_T("%s"),csTemp);

 

       if (0 == wcslen(g_szSharedRequestAndResultBuffer))

       {

              return;

       }

 

       //通知服务器可以进行反转了

       SetEvent(g_heventRequest);

       //等待服务器操作完成

       WaitForSingleObject(g_heventResult,INFINITE);

       //显示结果

       SetDlgItemText(IDC_EDIT_RESULT,g_szSharedRequestAndResultBuffer);

}

 

4)对话框退出的处理:

 

void CHandShakeDlg::OnDestroy()

{

       //通知服务器停止操作

       wcscpy(g_szSharedRequestAndResultBuffer,g_szServerShutdown);

       SetEvent(g_heventRequest);

       HANDLE h[2];

       h[0] = hThreadServer;

       h[1] = g_heventResult;

       //等待服务器停止成功

       WaitForMultipleObjects(2,h,TRUE,INFINITE);

       CloseHandle(hThreadServer);

       CloseHandle(g_heventResult);

       CloseHandle(g_heventRequest);

       CDialog::OnDestroy();

}

 

5、等待定时器内核对象。

在某个时间活按规定的时间间隔发出自己的信号通知的内核对象。

CreateWaitableTimer函数

hTimer参数用于指明要设置的定时器;pDueTime和lPeriod是配套使用的,pDueTimer指明定时器何时第一次报时,lPeriod用于指明此后定时器应该间隔多久报时一次。

举例:从2011-12-5 22:02:00开始,每隔5s打印出当前时间+“豆浆爱蟹儿”
程序运行结果如下:

程序代码为:

int _tmain(int argc, _TCHAR* argv[])

{

       HANDLE hTimer;

       SYSTEMTIME systime,localtime;

       FILETIME ftLocal,ftUTC;

       LARGE_INTEGER liUTC;

 

       //创建自动重置的定时器内核对象

       hTimer = CreateWaitableTimer(NULL,FALSE,NULL);

 

       systime.wYear = 2011;//

       systime.wMonth = 12;//

       systime.wDay = 5;//

       systime.wDayOfWeek = 1;//周一

       systime.wHour = 22;//10 PM

       systime.wMinute = 02;//1

       systime.wSecond = 0;

       systime.wMilliseconds = 0;

 

       //时间的转换

       SystemTimeToFileTime(&systime,&ftLocal);

       LocalFileTimeToFileTime(&ftLocal,&ftUTC);

       liUTC.LowPart = ftUTC.dwLowDateTime;

       liUTC.HighPart = ftUTC.dwHighDateTime;

 

       //s后报时一次

       SetWaitableTimer(hTimer,&liUTC,5000,NULL,NULL,FALSE);

 

       //只要等待的通知不是FAILED就一直循环下去

       while (WAIT_FAILED != WaitForSingleObject(hTimer,INFINITE))

       {

              GetLocalTime(&localtime);

              cout<<localtime.wYear<<'\\'<<localtime.wMonth<<'\\'<<localtime.wDay<<"    "

                     <<localtime.wHour<<':'<<localtime.wMinute<<':'<<localtime.wSecond

                     <<"豆浆爱蟹儿"<<endl;

       }

       return 0;

}

我们还有另外一种方式,即让等待定时器给APC项排队
正常情况下,我们同时给pfnCompletionRoutine和pvArgCompletionRoutine参数传递NULL,SetWaitableTime函数看到这些参数的NULL时,
到达规定的时间,就向定时器发出通知信号。我们也可以在到达时间时,把CPU交给APC
如下:

VOID APIENTRY TimerAPCRoutine(PVOID pvArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue)

{

       SYSTEMTIME localtime;

       GetLocalTime(&localtime);

       cout<<localtime.wYear<<'\\'<<localtime.wMonth<<'\\'<<localtime.wDay<<"    "

              <<localtime.wHour<<':'<<localtime.wMinute<<':'<<localtime.wSecond

              <<"豆浆爱蟹儿"<<endl;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

       HANDLE hTimer;

       SYSTEMTIME systime,localtime;

       FILETIME ftLocal,ftUTC;

       LARGE_INTEGER liUTC;

 

       //创建自动重置的定时器内核对象

       hTimer = CreateWaitableTimer(NULL,TRUE,NULL);

 

       systime.wYear = 2011;//

       systime.wMonth = 12;//

       systime.wDay = 5;//

       systime.wDayOfWeek = 1;//周一

       systime.wHour = 17;//4 PM

       systime.wMinute = 22;//44

       systime.wSecond = 0;

       systime.wMilliseconds = 0;

 

       //时间的转换

       SystemTimeToFileTime(&systime,&ftLocal);

       LocalFileTimeToFileTime(&ftLocal,&ftUTC);

       liUTC.LowPart = ftUTC.dwLowDateTime;

       liUTC.HighPart = ftUTC.dwHighDateTime;

 

       //s后报时一次

       SetWaitableTimer(hTimer,&liUTC,5000,TimerAPCRoutine,NULL,FALSE);

 

       while(TRUE)

       {

              SleepEx(INFINITE,TRUE);

       }

       return 0;

}

6、信标内核对象:
与所有内核对象一样,包含一个使用数量。但是它们也包含另外两个带符号的32位值,一个是最大资源数量(标示信标能够控制的资源的最大数量),
一个是当前资源数量(当前可以使用的资源的数量)。

7、互斥对象内核对象:
确保线程拥有对单个资源的互斥访问权。互斥对象包含一个使用数量,一个线程ID和一个递归计数器。行为特征与关键代码段相同,但是互斥对象
属于内核对象,而关键代码段则属于用户方式对象。所以不同进程中的多个线程能够访问当个互斥对象。
ID用户标识系统中的哪个线程当前拥有互斥对象,递归计数器用户指明该线程拥有互斥对象的次数。
使用规则:
1、如果线程ID是0,互斥对象不被任何线程所拥有,并发出该互斥对象的通知信号;
2、如果ID是非0,那么一个线程就拥有该互斥对象,且不发出该互斥对象的通知信号;
3、与其他内核对象不同,互斥对象在操作系统中拥有特殊的代码。

HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa,BOOL fInitialOwner,PCTSTR pszName);fInitialOwner置为FALSE,说明互斥对象没有被
任何线程使用,如果置为TRUE,互斥对象被调用它的线程所使用。
互斥对象拥有“线程所有权”的概念,能够记住哪个线程成功地等待到该对象。与其他对象不同,互斥对象返回的不是WAIT_OBJECT_0而是
WAIT_ABANDONED(只适用互斥对象),用于指明线程正在等待的互斥对象是由另一个线程拥有的,而另一个线程已经在完成对共享资源的使用前终
止运行。此时共享资源可能已经被破坏了。所以我们一般不检查WAIT_ABANDONED。

8、异步设备I/O
使得线程启动一个读操作或者写操作,但是不必等待操作完成。
WaitForInputIdle 该函数一直处于等待状态,直到进程在创建应用程序的第一个窗口的线程中已经没有尚未处理的输入为止。
MsgWaitForMultipleObjects来让线程等待自己的消息。
SingleObjectAndWait用与在单个原子方式的操作中发出关于内核对象的通知并且等待用户的另一个内核对象。其第一个参数必须标识为
一个互斥对象、信标对象或者事件。第二个参数标识:互斥对象、信标、事件、定时器、进程、线程、作业、控制台输入和修改通知。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值