什么是基于栈式同步?这是一种完全基于异步环境的线程处理之间的同步,可能这让人有些难以理解,但我们想想一下当你开辟了多条线程用于处理某种事物的时候,例如你访问 Redis 中的 Set 接口,大多数的客户端都需要阻塞当前线程,由另一个线程发射信号通知内核停止阻塞(恢复线程的上下文),但当这个同时阻塞的线程数量过多的时候,对内核与处理器的负载是很重的,而“基于栈式同步”是可以解决这些问题的。
但“基于栈式同步”本身存在很多的局限性,虽然它最好的实现是基于“自选锁”,其本身处理对锁的占有时间足够短(是一个链式结构)但不可否认的是,它具有 “爆栈(栈溢出)” 的问题,但可以通过一些手段进行有效的控制。
本文中提出的代码来自本人 github 上的开源项目 “malock CA*”,它参照了 “AutoResetEvent(自动重置内核事件对象)”的接口设计,是一种不可 “重入” 的锁。
参考源:https://github.com/liulilittle/malock
还有另一个致命的问题,就是说这种类型锁,它并不是很适合 “C/C++” 这类型的语言,这是由于 “语言” 本身的特性造成的,但它却很适合 “C# / VB.NET / Java” 这些语言,这不仅仅是 lambda 底层实现机制的问题。
这是一个“基于栈式”的锁,典型应用案例,从上述代码可以很直接的反应上面提到的一些问题,但这类的锁虽然是“完全异步” 的,的确可以获取到更好的效率与能耗,但也是因为这个特性,会促使它必须依赖于实现 “语言” 的本身特性才可以,否则编码复杂度的成本就不能被忽略,另一点它依赖了某些处理线程的堆栈,这就好比说虽然它异步处理,但最终执行还是需要依赖某个具体的执行线程的。
或许你可能会想到它单独运行了一个线程,但这是错误错的(单独建立线程会增大资源的消耗降低锁的效能,而且意义不是很大),它是利用诱发 “Set” 信号的线程进行处理的,例如:它可能是第一次调用 WaitOne 时,锁的原子信号处于 Set 态,那么处理线程就是第一次调用 WaitOne 的线程,但在一瞬间增加了大量需要 “异步同步” 的 WaitOne,处理线程可能会处于活动的状态,就是说有四条调用 Set 的线程,那么这四条线程可能都会执行不属于本身的 “WaitOne” 临界区内的任务
你可以从此获取到 “ https://github.com/liulilittle/malock/blob/master/malock/Core/StackAutoResetEvent.cs ” 的实现源,但是既然知道有 “爆栈” 方面的问题,难道就无法根治了?所以在 malock 的实现中提供了另外一种的 “基于栈式同步” 的锁的实现,用于解决这个问题。
namespace malock_client
{
using malock.Core;
using System;
using System.Threading;class Program
{
static int num = 0;static void Main(string[] args)
{
StackAutoResetEvent events = new StackAutoResetEvent(false);
for (int i = 0; i < 20; i++)
{
new Thread(() => events.WaitOne(() =>
{
Console.WriteLine(++num);
events.Set();
})).Start();
}
events.Set();
Console.ReadLine();
}
}
}
AsyncAutoResetEvent 类用以解决这个问题,它与 “StackAutoResetEvent” 的实现原理几乎大同小易,不过它利用一种状态标识用于控制处理的线程归属与 “暴栈”,但它有一个不太好的地方就是说,它可能会让同一条执行线程处理所有的临界区任务,这是一种比较坏的情况,当然持有锁的时间过程都不被释放本身应用锁的 “代码” 就存在很多的问题,这不是选用任何种锁解决解决的,而是需要从源头与根本上解决问题,才是正确的。
namespace malock_client
{
using malock.Core;
using System;
using System.Threading;class Program
{
static int num = 0;static void Main(string[] args)
{
AsyncAutoResetEvent events = new AsyncAutoResetEvent(false);
for (int i = 0; i < 20; i++)
{
new Thread(() => events.WaitOne((state) =>
{
Console.WriteLine(++num);
events.Set(state);
})).Start();
}
events.Set(true);
Console.ReadLine();
}
}
}