自己实现一个Semaphore
2007-12-15 22:15
302 查看
其实这是我boss的想法,我一开始听他这么说也觉得比较差异,ms已经写好了何必再自己写一个.答案有两个:1MS写的东西未必就是最好的,如完成端口,heap等.2semaphore是多线程编程中的核心元素所以有必要提速.我们都知道在多线程中ms提供的多个现成阻塞核心对象中critical(monitor),mutex,semaphore,event(EventWaitHandle),event的代价是最小的,所以我们就选择用event来实现semaphore.
event是一个不完整的semaphore,它不能连续唤醒多个阻塞的线程.而为了能够自动的,连续的唤醒阻塞的线程,我想到了半自动步枪的发射原理:当枪膛中的一颗子弹发射后由于后座力会使枪机在自动的从弹夹中压入一个子弹进入枪膛.将这个想法运用到这里就是:当一个线程被唤醒后就相当于打出一颗子弹,它需要再激发一次event已达到自动压子弹的目的.
当然仅使用event是不够的,因为还要处理枪膛里是否有子弹和当前子弹数这两个临界条件.而为了提高性能我们不能使用普通的锁(critical或mutex)还有一个原因是我说过semaphore是多线程编程中最核心的元素,锁就应该用它来实现.因此在这里我们选择使用Interlocked(内锁,它的性能很高在纳秒级别.通过降低控制粒度可以很好的提高并发程序的效率)实际上在这个设计中event只是为了让线程阻塞用的,而这两个临界条件才是关键.
下面进入代码部分: 我们需要三个成员变量,是否可以开火(枪膛中是否有子弹),子弹数和押子弹
int _canFire; //这里不用bool是因为 Interlocked.Exchange方法不接受bool类型
long _bulletCount;
EventWaitHandle _clipBullet;
public Semaphore() : this(0) { }
public Semaphore(int size) //我认为Semaphore的最大可用数为无限
{
this._clipBullet = new EventWaitHandle(false, EventResetMode.AutoReset);
this.Post(size);
}
private void LoadBullet() //子弹上膛
{
if (Atomic.Exchange(ref this._canFire, 1) == 0) //如果子弹上膛了就不触发事件
this._clipBullet.Set();
}
public void Release()
{
Interlocked.Increase(ref this._bulletCount); //增加子弹数,对于多于1的使用add方法
this.LoadBullet();
}
public void Release(int count)
{
Interlocked.Add(ref this._bulletCount, (long)count);
if (count > 0)
this.LoadBullet();
}
public bool WaitOne()
{
//检查枪膛里是否有子弹,如果没有等待押子弹事件
for (; Interlocked.Exchange(ref this._canFire, 0) == 0; )
{
this._clipBullet.WaitOne();
}
if (Atomic.Decrement(ref this._bulletCount) > 0) //如果还有子弹则自动压入一颗
this.LoadBullet();
return true;
}
public bool Try_Wait() //尝试等待,相当于WaitOne(0) 这是一个很常用的功能所以要单独处理
{
if (Interlocked.CompareExchange(ref this._canFire, 0, 1) != 1) //这个函数就是大名鼎鼎的CAS
return false;
if (Interlocked.Decrement(ref this._bulletCount) > 0)
this.LoadBullet();
return true;
}
在c++环境下测试比windows原始semaphore快将近2被,在.net下没有明显效果.我觉得这和虚机的调度实现有关系.
event是一个不完整的semaphore,它不能连续唤醒多个阻塞的线程.而为了能够自动的,连续的唤醒阻塞的线程,我想到了半自动步枪的发射原理:当枪膛中的一颗子弹发射后由于后座力会使枪机在自动的从弹夹中压入一个子弹进入枪膛.将这个想法运用到这里就是:当一个线程被唤醒后就相当于打出一颗子弹,它需要再激发一次event已达到自动压子弹的目的.
当然仅使用event是不够的,因为还要处理枪膛里是否有子弹和当前子弹数这两个临界条件.而为了提高性能我们不能使用普通的锁(critical或mutex)还有一个原因是我说过semaphore是多线程编程中最核心的元素,锁就应该用它来实现.因此在这里我们选择使用Interlocked(内锁,它的性能很高在纳秒级别.通过降低控制粒度可以很好的提高并发程序的效率)实际上在这个设计中event只是为了让线程阻塞用的,而这两个临界条件才是关键.
下面进入代码部分: 我们需要三个成员变量,是否可以开火(枪膛中是否有子弹),子弹数和押子弹
int _canFire; //这里不用bool是因为 Interlocked.Exchange方法不接受bool类型
long _bulletCount;
EventWaitHandle _clipBullet;
public Semaphore() : this(0) { }
public Semaphore(int size) //我认为Semaphore的最大可用数为无限
{
this._clipBullet = new EventWaitHandle(false, EventResetMode.AutoReset);
this.Post(size);
}
private void LoadBullet() //子弹上膛
{
if (Atomic.Exchange(ref this._canFire, 1) == 0) //如果子弹上膛了就不触发事件
this._clipBullet.Set();
}
public void Release()
{
Interlocked.Increase(ref this._bulletCount); //增加子弹数,对于多于1的使用add方法
this.LoadBullet();
}
public void Release(int count)
{
Interlocked.Add(ref this._bulletCount, (long)count);
if (count > 0)
this.LoadBullet();
}
public bool WaitOne()
{
//检查枪膛里是否有子弹,如果没有等待押子弹事件
for (; Interlocked.Exchange(ref this._canFire, 0) == 0; )
{
this._clipBullet.WaitOne();
}
if (Atomic.Decrement(ref this._bulletCount) > 0) //如果还有子弹则自动压入一颗
this.LoadBullet();
return true;
}
public bool Try_Wait() //尝试等待,相当于WaitOne(0) 这是一个很常用的功能所以要单独处理
{
if (Interlocked.CompareExchange(ref this._canFire, 0, 1) != 1) //这个函数就是大名鼎鼎的CAS
return false;
if (Interlocked.Decrement(ref this._bulletCount) > 0)
this.LoadBullet();
return true;
}
在c++环境下测试比windows原始semaphore快将近2被,在.net下没有明显效果.我觉得这和虚机的调度实现有关系.
相关文章推荐
- go 自己实现一个gob编解码
- 实现一个函数,打印乘法口诀表,口诀表的行数和列数自己指定, 输入9,输出9*9口诀表,输出12,输出12*12的乘法口诀表。
- 利用typeof实现一个自己的sizeof
- 根据源码自己实现一个容器的空间适配器
- 创建一个数组, 实现函数init()初始化数组、 实现empty()清空数组、 实现reverse()函数完成数组元素的逆置。 要求:自己设计函数的参数,返回值。
- 创建一个数组, 实现函数init()初始化数组、 实现empty()清空数组、 实现reverse()函数完成数组元素的逆置。 要求:自己设计函数的参数,返回值。
- 咋 实现点击一个按钮 就progressDialog 出来自己转20秒 就dismiss
- 最近在玩android,突然想做一个私人聊天工具,缺乏一个服务器,于是自己想写一个c实现的daemon select server
- 自己实现一个简单的Mybatis框架
- 彻底理解Promise对象——用es5语法实现一个自己的Promise(上篇)
- 自己实现一个String类
- 自己实现 一个MapReduce 示例
- 自己实现一个数的整数次方 11
- 尝试自己写一个工具类实现UGUI的按钮功能
- 新手提问 想要做一个单机android应用,要实现注册登录功能。目前折在了连数据库这一步上,也不知道自己这么做对不对,哪儿错了。求教求教!
- 实现过程全纪录——自己写一个“微信朋友圈”(包括移动端与PC端)
- 实现一个函数,打印乘法口诀表,口诀表的行数和列数自己指定
- 自己动手实现一个mvvm库
- <仅是自己做笔记。。。系列15>实现一个挺高级的字符匹配算法: 给一串很长字符串,要求找到符合要求的字符串,例如目的串:123 1******3***2 ,12*****3这些都要找出来
- 手写一个自己的LocalCache - 基于LinkedHashMap实现LRU