电信主站 网通分站
购买流程 付款方式 常见问题 在线提问 续租服务 购物车
用户名: 密 码: 忘记密码?
首 页
域名注册
虚拟主机
双线主机
服务器租用
VPS主机
企业邮局
代理专区
客服中心
虚拟主机行业资讯 虚拟主机评测对比 互联网最新动态 技术学院 站长资讯 在线教程 网站运营
搜索优化 服务器 网络编程 图形图象 站长之家 网页制作 操作系统
冲浪宝典 软件教学 视频通信 办公软件 邮件系统 网络安全 认证考试
您当前位置:西部数码->资讯中心-> 在线教程-> ASP
一种Windows下线程同步的实现方法-ASP教程,系统相关
作者:网友供稿 点击:29
  西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!虚拟主机可在线rar解压,自动数据恢复设置虚拟目录等.虚拟主机免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金!
文章页数:[1] 

一种windows下线程同步的实现方法

windows下的多线程与线程同步概述

多任务是一个操作系统可以同时运行多个程序的能力。基本上,操作系统使用一个硬件时钟为同时运行的每个进程分配“时间片”。如果时间片足够小,并且机器也没有由于太多的程序而超负荷,那么在用户看来,所有的这些程序似乎在同试运行着。

多线程是在一个程序内部实现多任务的能力。程序可以把它自己分隔为单独的执行“线程”,这些线程似乎也同时在运行[1][g1] 多线程的应用非常广泛,最常见的是在需要进行大量计算的程序中使用辅助线程完成计算工作,而用户界面线程响应用户的操作。

多线程中不同线程之间的通讯通常是使用共享数据对象来实现的,不管是使用全局变量还是线程过程函数的指针参数进行通讯,都可能引发访问冲突[2][g2] 而解决这一问题的方法即线程同步。

windows提供了多种方法来实现线程之间的协调和同步,有临界区、事件对象、互斥量等。这些方法都有各自的特点和适用的场合,下面就让我们看一下经典之作windows程序设计》一书中所介绍的使用临界区进行线程同步的方法 [3]

首先需要定义一个全局的临界区对象,以便在不同的线程中能够访问。例如:critical_section cs;

然后在某个线程中初始化这个临界区对象:

initializecriticalsection(&cs);

这样就创建了一个名为cs的临界区对象。此时,线程可以通过下面的调用进入临界区:

entercriticalsection(&cs);

在这时,线程被认为“拥有”临界区对象。没有两个线程可以同时拥有临界区对象,因此,如果一个线程进入了临界区,那么下一个使用同一临界区对象调用entercriticalsection的线程将在函数调用中被挂起。只有当第一个线程通过下面的调用离开临界区时,函数才会返回:

leavecriticalsection(&cs);

这时,在entercriticalsection的调用中被挂起的线程拥有临界区,其函数调用也返回,允许线程继续运行。

当临界区不再需要时,可以调用:

deletecriticalsection(&cs);

将其删除。[g3] 

在进入临界区后,线程可以独占方式访问资源而不用担心其他线程的干扰,当不同的线程共享不同的数据时,还可以通过使用多个临界区来实现。

事件对象和互斥量等在使用上与临界区有所不同,但流程和步骤相似,只不过需要调用waitforsingleobject来替代entercriticalsection函数来阻塞线程,并等待其他线程在执行完毕后释放资源。

       可以看出,线程同步是一件比较复杂而又容易出错的工作,既要保证各线程在访问和更新数据中不会冲突,又要防止死锁。在mfc中,为了降低线程同步的复杂性,减少工作量,提供了ccriticalsectionceventcmutex等类封装了windows api中相关的线程同步函数,方便编程人员使用[4][g4] 但是这些类的出现并没有改变基本的线程同步编程的流程和步骤,在具体使用过程中仍需要小心谨慎的使用才能够达到目的。

 

一种简便的线程同步的实现方法

     能否在windows提供的线程同步的方法上进行改进,提供一种简单方便的实现方法呢,这种方法应该不需要繁琐的步骤,既能够满足我们的要求,同时又足够的灵活。

     首先让我们重新审视一下windows所提供的几种线程同步机制。可以发现,在事件对象、互斥量和信号量的使用中都可以使用一个字符串作为线程同步对象的标识,当创建一个带有名称标识的线程同步对象时,若已存在同名对象则会返回一个已存在对象的句柄,并且可以通过调用getlasterror获得error_already_exists值来检验是否返回了一个已存在对象的句柄值。

       以互斥量举例来说:

       //创建第一个互斥量

       handle mutex1;     

       mutex1 = createmutex(null, true, "mutex");

       if(error_already_exists == getlasterror())   //存在同名互斥量

       {

              printf("mutex exist!\n");

       }

       else //未发现同名互斥量

       {

              printf("create mutex!\n");

       }

 

       //创建第二个互斥量

       handle mutex2;

       mutex2 = createmutex(null, true, "mutex");

       if(error_already_exists == getlasterror())   //存在同名互斥量

       {

              printf("mutex exist!\n");

       }

       else //未发现同名互斥量

       {

              printf("create mutex!\n");

}

我们在vc++6.0中编译并运行上述代码,得到的结果输出为:

create mutex!

mutex exist!

从而可以看出,当我们使用“mutex”作为名称多次创建互斥量时,通过检查getlasterror的返回值可判断出是否是第一次创建此名称的互斥量。由此便可进一步发展出下面两个函数:

handle lock(char* name)

{

       handle mutex;

       // try to open an exist mutex firstly.

       mutex = openmutex(mutex_all_access, false, name);

       if(null == mutex)     // if the mutex does not exist, create it with the certain name.

       {

              mutex = createmutex(null, true, name);

       }

       else // if the mutex already exist, wait for other thread release it.

       {

              waitforsingleobject(mutex, infinite);

       }

       return mutex;

}

bool unlock(handle mutex)

{

       if(0 == releasemutex(mutex))   // failed to release mutex

       {

              return false;

       }

       else // successed in release mutex

       {

              closehandle(mutex);

              mutex = null;

              return true;

       }

}

     使用时仅需进行如下调用:

       handle mutex = lock("mutexlockname");

       ……

       unlock(mutex);

       在调用lock时传入互斥量的名称。当不存在同名的互斥量时调用createmutex创建一个以name变量值为名称的互斥量;若同名的互斥量已经存在时openmutex函数将返回已存在的互斥量的一个句柄,此时通过调用waitforsingleobject阻塞当前线程,等待同名互斥量被释放后继续运行。

       在调用unlock时传入互斥量的句柄,通过releasemutex释放线程对互斥量的拥有权,并关闭句柄,防止资源泄露。

     有了上述两个函数,我们便可通过对同一资源使用相同的名称来进行锁定,实现线程同步了。例如在不同的线程中存在着一个对象,定义如下:

cobject object;

当我们在访问和更改这一对象时只需进行如下调用:

       handle mutex = lock("object");

       object.dosomething();

       unlock(mutex);

     就可以十分方便的完成针对这一对象的线程同步了。

 

     通过lockunlock函数,我们隐藏了使用互斥量时的一些具体细节,降低了复杂性,但是对于这种方法仍存在着许多不足,下面就一步一步循序渐进的来改进这些问题。

 

在使用过程中,必须为lock函数传入一个名称作为参数,并且此名称在锁定同一数据对象时必须是一致的才能保证lock函数的正常运作,这就为用户带来了不必要的麻烦。当存在如下声明时:

cobject* pobject1 = new cobject;

cobject* pobject2 = pobject1;

cobject* pobject3 = pobject2;

可以看出,pobject1pobject2pobject3实际上指向同一对象,这时候如果要进行线程同步必须在锁定这三个指针时使用相同的名称标识。于是就出现了别名问题。

     c++中,在不同的作用域中变量名称是可以相同的,因此同样一个pobject在不同的作用域中可能是指向不同的对象的,也就是同名问题。

针对别名和同名,如何能够简单的识别出同一对象并且为其统一命名呢?在c++ 中每个类都存在一个this指针用以指向自身存储的地址。在一个程序中,每个不同的对象在内存中都有着不同的存储地址。这个地址就好像身份证号码一样标识出了不同的对象,也正是我们所希望得到的针对不同对象的唯一的名称。于是可以将lock函数改造为:

handle lock(void* pointer)

{

       char name[128];

       itoa((dword)pointer, name, 16);

 

// 以下同前述

       handle mutex;

       // try to open an exist mutex firstly.

       mutex = openmutex(mutex_all_access, false, name);

       ……

}

这样在调用时就不需要再人为的为每个对象命名了,只需要将对象的地址传入即可:

       handle mutex = lock(pobject);

       ……

       unlock(mutex);

       从而也就免去了命名之苦。

 

       最主要的问题已经通过努力一步步地解决了,但是在程序看起来却依然很丑陋,lockunlock必须作为全局函数,有违面向对象的设计原则,而且每次需要成对的调用,难免会有粗心的程序员调用了lock[g5] 却忘记unlock,于是程序就有可能进入死锁[ms6] 

为了能够更方便的使用,首先创建一个类cmutexlock,将lockunlock作为成员函数,再重新审视前面的程序,不难发现unlock的参数handle mutex也可以移入cmutexlock成为其成员变量:

class cmutexlock 

{

public:

       cmutexlock();

       virtual ~cmutexlock();

       bool lock(void* pointer);

       bool unlock();

 

private:

       handle m_mutex;

};

     这时,在使用过程中只需先声明一个cmutexlock的对象,然后调用lockunlock函数。

     lockunlock成为一个类的成员函数时,随之而来的一个疑问就是:

“有必要暴露这两个函数成为公共方法吗?”既然lockunlock必须成对的出现,那么不是刚好对应于类的构造函数和析构函数吗?为什么不把它们一个放入构造函数,一个放入析构函数呢?当提出了这些疑问的同时也就找到了解决之道:

     class cmutexlock 

{

public:

       cmutexlock(void* pointer);

       virtual ~cmutexlock();

 

private:

       bool lock(void* pointer);

       bool unlock();

       handle m_mutex;

};

    

cmutexlock::cmutexlock(void* pointer)

:m_mutex(null)

{

       this->lock(pointer);

}

 

cmutexlock::~cmutexlock()

{

       this->unlock();

}

 

……

cmutexlock的调用也简化为了一句定义语句:

{

cmutexlock lock(pobject);

     ……

}

当声明lock时,针对传入构造函数的pobject调用lock函数,当lock离开生存空间销毁时在析构函数里面调用unlock释放互斥量。在这里通过使用{}来控制cmutexlock析构函数的调用,即释放对pobject的锁定。在c++{}可以用来控制变量的作用域。在{}内声明的lock在遇到}时即会被销毁,在销毁前析构函数也将会被自动调用。

     至此,我们就完成了一个使用起来简单方便的线程同步辅助类。

小结

       在本文中,以windows多线程机制为基础,利用c++的一些最基本的特性一步步的创建出了cmutexlock类,而线程同步也由最初繁琐复杂的过程简化至只需一对{}和一句定义语句就完成了。cmutexlock使用起来简单方便,可以针对不同的数据对象分别进行锁定,并且减少了死锁的出现。在实际程序中运作良好,表明了这种方法的实用性。

       但是在使用过程中,由于互斥量本身特性和本文中实现方式带来的一些弊病在所难免,例如不同进程之间的名字冲突、效率问题、构造函数发生错误、别名问题等。限于篇幅和笔者水平,这些问题不在此文中一一详述。如有兴趣,可来信讨论,邮件地址:deeplymove@tom.com

      

参考文献:

[1] p1119 windows程序设计》(第5版) charles petzold 北京大学出版社
[2] p263 visual c++ 6.0 技术内幕》(第5版修订版) david j.kruglinski scot wingo george shepherd 希望出版社

[3] p1147 windows程序设计》(第5版) charles petzold 北京大学出版社

[4] msdn

 

 


 [g1]p1119 windows程序设计》(第5版) charles petzold 北京大学出版社

 [g2]p263 visual c++ 6.0 技术内幕》(第5版修订版) david j.kruglinski scot wingo george shepherd 希望出版社

 

 [g3]p1147 windows程序设计》(第5版) charles petzold 北京大学出版社

 [g4]msdn

 [g5]

 [ms6]重构

english translation:

 http://translate.google.com/translate?u=http://blog.csdn.net/deeplymove/archive/2006/11/19/1395987.aspx&langpair=zh-cn%7cen&hl=en&ie=utf8


文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
相关主题
文章页数:[1] 
Google
热门文章
·如何使XP的目录属性出现"安全"选项-ASP教程,系统相关
·创建有个性的对话框之MFC篇(二)-ASP教程,系统相关
·用InstallShield打包ASP程序-ASP教程,ASP应用
·windows server 2003 中 SQL Server 2000 分布式事务 错误解决方法-ASP教程,系统相关
·创建有个性的对话框之MFC篇(一)-ASP教程,系统相关
·DevExpress打印相关代码-ASP教程,打印相关
·File文件控件,选中文件(图片,flash,视频)即立即预览显示-ASP教程,组件开发
·用Windows的文件映射机制,实现大批量数据的快速存储-ASP教程,系统相关
·ADO如何取得数据库中表的字段信息之一
·使用DEVEXPRESS部件打印时标题的处理-ASP教程,打印相关

最新文章
· SQL注入天书 - ASP注入漏洞全接触
·用.net 处理xmlHttp发送异步请求
·asp.net创建文件夹的IO类的问题
·如何实现ASP.NET网站个性化
·关于ASP.NET调用JavaScript的实现
·ASP利用Google实现在线翻译功能
·Asp无组件生成缩略图
·由HTTP 500 Internal server error想到的...
·实例讲解asp抓取网上房产信息
·改mdb为asp所带来的灾难


 
 


版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!

特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
  打印  刷新  关闭
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 虚拟主机 | 资讯中心 | 友情链接 | 网站地图

版权所有 西部数码(www.west263.com)
CopyRight (c) 2002~2006 west263.com all right reserved.
公司地址:四川成都市万和路90号天象大厦4楼 邮编:610031
电话总机:028-86262244 86263048 86263408 86263960 86264018 86267838
售前咨询:总机转201 202 203 204 206 208
售后服务:总机转211 212 213 214
财务咨询:总机转224 223 传真:028-86264041 财务QQ:点击发送消息给对方635483282
售前咨询QQ:点击发送消息给对方2182518 点击发送消息给对方241975952 点击发送消息给对方275026793 点击发送消息给对方408235859
售后服务QQ:点击发送消息给对方17708515 点击发送消息给对方307742704 点击发送消息给对方287976517 点击发送消息给对方363783715
《中华人民共和国增值电信业务经营许可证》编号:川B2-20030065号