数据库连接池的计数器设计
时间:2022-03-10 17:08
设计过ORM的攻城狮们或多或少都应该考虑过连接池,考虑过连接池就或多或少会想过计数器....
<计数器在连接池中的应用>
曾经在我设计一套ORM的时候想过这样一种连接池的管理方式:
- 0.连接池中的所有连接都对应存在一个计数器,指示连接目前被多少对象引用;
当计数器从0变成1的时候,打开连接Connection.Open();
当计数器从1变成0的时候,关闭连接Connection.Close(); - 1.连接池中有一个默认连接DefaultConnection,这个连接被所有的非事务操作共用,比如查(select);
- 2.当发生一个查询操作的时候,先获得DefaultConnection,同时对应计数器+1,使用完之后DefaultConnection的计数器-1;
- 3.当发生事务操作的时候,会从连接池中申请一个连接数为0的Connection(但不会是DefaultConnection);
如果连接池中不存在这样的连接,则会新建一个并加入到连接池中;
获得Connection后对应计数器+1,使用完之后对应计数器-1; - 4.如果申请事务操作时连接池已达到上限,且所有连接的计数器都大于1,则请求进入队列,直至得到Connection或超时;
<计数器1.0>
第一版的设计非常的简单,直接就是类似于这样的
ps:以下为示例代码,用意是便于理解,请不要太较真
using System; using System.Collections.Generic; using System.Text; using System.Threading; namespace blqw { /// <summary> 计数器,具有单线程模式和多线程模式 /// </summary> public sealed class Counter { /// <summary> 构造一个计数器,默认单线程模式 /// <para>无论在任何线程中每次执行Add方法都会增加引用数,执行Remove或者token.Dispose都会减少引用数</para> /// </summary> public Counter() :this(false) { Console.WriteLine(); } /// <summary> 构造一个计数器,根据参数multiThreadMode确定是否使用多线程模式 /// <para>多线程模式:在相同线程中,只有第一次执行Add方法时增加引用数,也只有此token被Remove或Dispose才会减少引用数</para> /// </summary> /// <param name="multiThreadMode"></param> public Counter(bool multiThreadMode) { if (multiThreadMode) { _dataSlot = Thread.AllocateDataSlot(); } } /// <summary> 当前引用数 /// </summary> private int _value; /// <summary> 值改变事件 /// </summary> private EventHandler<CounterChangedEventArgs> _valueChanged; /// <summary> 用于储存多线程间的独立数据,多线程模式下有值 /// </summary> private LocalDataStoreSlot _dataSlot; /// <summary> 增加引用,并获取用于释放引用的标记 /// </summary> public IDisposable Add() { if (_dataSlot != null) { //获取当前线程中的值,此方法每个线程中获得的值都不同,不需要线程同步 //如果已经存在,则不计数 if (Thread.GetData(_dataSlot) != null) { return null; } Thread.SetData(_dataSlot, string.Empty); } return new CounterToken(this); } /// <summary> 减少引用 /// </summary> /// <param name="token">通过Add方法获取的标记对象</param> public void Remove(IDisposable token) { if (token == null) { return; } if (token is CounterToken == false) { throw new ArgumentException("参数不是一个有效的引用标记", "token"); } if (token.Equals(this) == false) { throw new ArgumentOutOfRangeException("token", "此标记不属于当前计数器"); } token.Dispose(); } /// <summary> 当前计数值 /// </summary> public int Value { get { return _value; } } /// <summary> 增加记数 /// </summary> private void OnIncrement() { var val = Interlocked.Increment(ref _value); OnValueChanged(val, val - 1); } /// <summary> 减少计数 /// </summary> private void OnDecrement() { if (_dataSlot != null) { Thread.SetData(_dataSlot, null); } var val = Interlocked.Decrement(ref _value); OnValueChanged(val, val + 1); } /// <summary> 触发ValueChaged事件 /// </summary> /// <param name="value">触发Value事件时Value的值</param> /// <param name="oldValue">触发Value事件之前Value的值</param> private void OnValueChanged(int value, int oldValue) { var handler = _valueChanged; if (handler != null) { var e = new CounterChangedEventArgs(value, oldValue); handler(this, e); } } /// <summary> 计数器值改变事件 /// </summary> public event EventHandler<CounterChangedEventArgs> ValueChanged { add { _valueChanged -= value; _valueChanged += value; } remove { _valueChanged -= value; } } /// <summary> 计数器引用标记,调用计数器的Add方法可获得该对象,释放对象时,减少计数器的计数值 /// </summary> sealed class CounterToken : IDisposable { /// <summary> 宿主计数器 /// </summary> private Counter _counter; /// <summary> 释放标记,0未释放,1已释放,2执行了析构函数 /// </summary> private int _disposeMark; /// <summary> 构造函数,创建引用标记并增加宿主计数器的值 /// </summary> /// <param name="counter">宿主计数器</param> public CounterToken(Counter counter) { if (counter == null) { throw new ArgumentNullException("counter"); } _counter = counter; _counter.OnIncrement(); _disposeMark = 0; } /// <summary> 析构函数 /// </summary> ~CounterToken() { //如果尚未释放对象(标记为0),则将标记改为2,否则标记不变 Interlocked.CompareExchange(ref _disposeMark, 2, 0); Dispose(); } /// <summary> 释放引用标记,并减少宿主计数器的值 /// </summary> public void Dispose() { //如果已释放(标记为1)则不执行任何操作 if (_disposeMark == 1) { return; } //将标记改为1,并返回修改之前的值 var mark = Interlocked.Exchange(ref _disposeMark, 1); //如果当前方法被多个线程同时执行,确保仅执行其中的一个 if (mark == 1) { return; } //释放Counter引用数 try { _counter.OnDecrement(); } catch { } _counter = null; //如果mark=0,则通知系统不需要执行析构函数了 if (mark == 0) { GC.SuppressFinalize(this); } } /// <summary> 重新实现比较的方法 /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(object obj) { if (obj is Counter) { return object.ReferenceEquals(this._counter, obj); } return object.ReferenceEquals(this, obj); } } } /// <summary> 计数器值改变事件的参数 /// </summary> public class CounterChangedEventArgs:EventArgs { internal CounterChangedEventArgs(int value,int oldValue) { Value = value; OldValue = oldValue; } /// <summary> 当前值 /// </summary> public int Value { get; private set; } /// <summary> 原值 /// </summary> public int OldValue { get; private set; } } }Counter完整代码
var counter = new Counter(true);//多线程模式 //var counter = new Counter(); //单线程模式 new Thread(() => { using (counter.Add()) //计数器+1 当前计数器=1 { Console.WriteLine("线程a:" + counter.Value); using (counter.Add()) //计数器不变 当前计数器=1 { Console.WriteLine("线程a:" + counter.Value); using (counter.Add()) //计数器不变 当前计数器=1 { Console.WriteLine("线程a:" + counter.Value); Thread.Sleep(100); //等待线程b执行,b执行完之后 当前计数器=1 } //计数器不变 当前计数器=1 Console.WriteLine("线程a:" + counter.Value); } //计数器不变 当前计数器=1 Console.WriteLine("线程a:" + counter.Value); } //计数器-1 当前计数器=0 Console.WriteLine("线程a:" + counter.Value); }).Start(); Thread.Sleep(50); new Thread(() => { var token1 = counter.Add(); //计数器+1 当前计数器=2 Console.WriteLine("线程b:" + counter.Value); var token2 = counter.Add(); //计数器不变 当前计数器=2 Console.WriteLine("线程b:" + counter.Value); var token3 = counter.Add(); //计数器不变 当前计数器=2 Console.WriteLine("线程b:" + counter.Value); counter.Remove(token3); //计数器不变 当前计数器=2 Console.WriteLine("线程b:" + counter.Value); counter.Remove(token2); //计数器不变 当前计数器=2 Console.WriteLine("线程b:" + counter.Value); counter.Remove(token1); //计数器-1 当前计数器=1 Console.WriteLine("线程b:" + counter.Value); }).Start(); Console.ReadLine();测试Demo
多线程模式测试结果
单线程模式测试结果
数据库连接池的计数器设计,布布扣,bubuko.com