×

Windows CE 进程、线程和内存管理二

消耗积分:1 | 格式:rar | 大小:0.3 MB | 2017-11-08

分享资料个

 Windows CE 进程、线程和内存管理(二)
  2006-12-09 17:50:44 来源:嵌入式资讯网
  分享到: 二、同步
  在多数情况下,线程之间难免要相互通信、相互协调才能完成任务。比如,当有多个线程共同访问同一个资源时,就必须保证一个线程正读取这个资源数据的时候,其它线程不能够修改它。这就需要线程之间相互通信,了解对方的行为。再有当一个线程要准备执行下一个任务之前,它必须等待另一个线程终止才能运行,这也需要彼此相互通信。实际开发过程中,线程间需要同步的情况非常多。Windows CE.NET给我们提供了很多的同步机制,熟练的掌握这些机制并合理运用会使线程之间的同步更合理、更高效。进程间的通信机制在下一篇文章中讲解。
  Windows CE.NET具有两种运行模式:用户模式和内核模式。并且允许一个运行于用户模式的应用程序随时切换为内核模式,或切换回来。线程同步的有些解决办法运行在用户模式,有些运行在内核模式。《Windows核心编程》上说从用户模式切换到内核模式再切换回来至少要1000个CPU周期。我查看过CE下API函数SetKMode的源码,这个函数用于在两种模式间切换,改变模式只需修改一些标志,至于需要多少个CPU周期很难确定。但至少可以肯定来回切换是需要一定时间的。所以在选择同步机制上应该优先考虑运行在用户模式的同步解决办法。
  1、互锁函数
  互锁函数运行在用户模式。它能保证当一个线程访问一个变量时,其它线程无法访问此变量,以确保变量值的唯一性。这种访问方式被称为原子访问。互锁函数及其功能见如下列表:
  函数参数和功能
  InterlockedIncrement参数为PLONG类型。此函数使一个LONG变量增1
  InterlockedDecrement参数为PLONG类型。此函数使一个LONG变量减1
  InterlockedExchangeAdd参数1为PLONG类型,参数2为LONG类型。此函数将参数2赋给参数1指向的值
  InterlockedExchange参数1为PLONG类型,参数2为LONG类型。此函数将参数2的值赋给参数1指向的值
  InterlockedExchangePointer参数为PVOID* 类型,参数2为PVOID类型。此函数功能同上。具体参见帮助
  InterlockedCompareExchange参数1为PLONG类型,参数2为LONG类型,参数3为LONG类型。此函数将参数1指向的值与参数3比较,相同则把参数2的值赋给参数1指向的值。不相同则不变
  InterlockedCompareExchangePointer参数1为PVOID* 类型,参数2为PVOID类型,参数3为PVOID。此函数功能同上。具体参见帮助
  2、临界区
  临界区对象运行在用户模式。它能保证在临界区内所有被访问的资源不被其它线程访问,直到当前线程执行完临界区代码。除了API外,MFC也对临界区函数进行了封装。临界区相关函数:
  void InitializeCriticalSection ( LPCRITICAL_SECTION );
  void EnterCriticalSection ( LPCRITICAL_SECTION );
  void LeaveCriticalSection ( LPCRITICAL_SECTION );
  void DeleteCriticalSection ( LPCRITICAL_SECTION );
  举例如下:
  void CriticalSectionExample (void)
  {
  CRITICAL_SECTION csMyCriticalSection;
  InitializeCriticalSection (&csMyCriticalSection); ///初始化临界区变量
  __try
  {
  EnterCriticalSection (&csMyCriticalSection); ///开始保护机制
  ///此处编写代码
  }
  __finally ///异常处理,无论是否异常都执行此段代码
  {
  LeaveCriticalSection (&csMyCriticalSection); ///撤销保护机制
  }
  }
  MFC类使用更简单:
  CCriticalSection cs;
  cs.Lock();
  ///编写代码
  cs.Unlock();
  使用临界区要注意的是避免死锁。当有两个线程,每个线程都有临界区,而且临界区保护的资源有相同的时候,这时就要在编写代码时多加考虑。
  3、事件对象
  事件对象运行在内核模式。与用户模式不同,内核模式下线程利用等待函数来等待所需要的事件、信号,这个等待过程由操作系统内核来完成,而线程处于睡眠状态,当接收到信号后,内核恢复线程的运行。内核模式的优点是线程在等待过程中并不浪费CPU时间,缺点是从用户模式切换到内核模式需要一定的时间,而且还要切换回来。在讲解事件对象前应该先谈谈等待函数。等待函数有四个。具体参数和功能见下表:
  函数参数和功能
  WaitForSingleObject参数1为HANDLE类型,参数2为DWORD类型。此函数等待参数1标识的事件,等待时间为参数2的值,单位ms。如果不超时,当事件成为有信号状态时,线程唤醒继续运行。
  WaitForMultipleObjects参数1为DWORD类型,参数2为HANDLE * 类型,参数3为BOOL类型,参数4为DWORD类型。此函数等待参数2指向的数组中包含的所有事件。如果不超时,当参数3为FALSE时,只要有一个事件处于有信号状态,函数就返回这个事件的索引。参数3为TRUE时,等待所有事件都处于有信号状态时才返回。
  MsgWaitForMultipleObjects参数1为DWORD类型,参数2为LPHANDLE类型,参数3为BOOL类型,参数4为DWORD类型,参数5为DWORD类型。此函数功能上同WaitForMultipleObjects函数相似,只是多了一个唤醒掩码。唤醒掩码都是和消息有关的。此函数不但能够为事件等待,还能为特定的消息等待。其实这个函数就是专为等待消息而定义的。
  MsgWaitForMultipleObjectsEx参数1为DWORD类型,参数2为LPHANDLE类型,参数3为DWORD类型,参数4为DWORD类型,参数5为DWORD类型。此函数是MsgWaitForMultipleObjects函数的扩展。将原来函数的参数3除掉,添加参数5为标志。标志有两个值:0或MWMO_INPUTAVAILABLE。
  如果一个线程既要执行大量任务同时又要响应用户的按键消息,这两个专用于等待消息的函数将非常有用。
  和事件有关的函数有:
  HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPTSTR lpName);
  BOOL SetEvent(HANDLE hEvent );
  BOOL PulseEvent(HANDLE hEvent);
  BOOL ResetEvent(HANDLE hEvent);
  HANDLE OpenEvent(DWORD dwDesiredAccess,
  BOOL bInheritHandle,
  LPCTSTR lpName );
  事件对象是最常用的内核模式同步方法。它包含一个使用计数和两个BOOL变量。其中一个BOOL变量指定这个事件对象是自动重置还是手工重置。另一个BOOL变量指定当前事件对象处于有信号状态还是无信号状态。
  函数CreateEvent创建一个事件对象,参数1必须为NULL,参数2指定是否手工重新设置事件对象的状态。如果为FALSE,当等待函数接到信号并返回后此事件对象被自动置为无信号状态。这时等待此事件对象的其它线程就不会被唤醒,因为事件对象已经被置为无信号状态。如果参数2设置为TRUE,当等待函数接到信号并返回后事件对象不会被自动置于无信号状态,其它等待此事件对象的线程都能够被唤醒。用ResetEvent函数可以手工将事件对象置为无信号状态。相反SetEvent函数将事件对象置为有信号状态。PulseEvent函数将事件对象置为有信号状态,然后立即置为无信号状态,在实际开发中这个函数很少使用。OpenEvent函数打开已经创建的事件对象,一般用于不同进程内的线程同步。在调用CreateEvent创建一个事件对象时,传递一个名字给参数4,这样在其它进程中的线程就可以调用OpenEvent函数并指定事件对象的名字,来访问这个事件对象。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !