func (l *Gradient2Limit) NotifyOnChange(consumer core.LimitChangeListener) {
	l.mu.Lock()
	l.listeners = append(l.listeners, consumer)
	l.mu.Unlock()
}
NotifyOnChange will register a callback to receive notification whenever the limit is updated to a new value.
package limit
import (
	"fmt"
	"math"
	"sync"
	"github.com/platinummonkey/go-concurrency-limits/core"
	"github.com/platinummonkey/go-concurrency-limits/measurements"
)
type Gradient2Limit struct {
	estimatedLimit float64
	shortRTT core.MeasurementInterface
	longRTT core.MeasurementInterface
	maxLimit int
	minLimit                int
	queueSizeFunc           func(limit int) int
	smoothing               float64
	commonSampler           *core.CommonMetricSampler
	longRTTSampleListener   core.MetricSampleListener
	shortRTTSampleListener  core.MetricSampleListener
	queueSizeSampleListener core.MetricSampleListener
	mu        sync.RWMutex
	listeners []core.LimitChangeListener
	logger    Logger
	registry  core.MetricRegistry
}
func NewDefaultGradient2Limit(
	name string,
	logger Logger,
	registry core.MetricRegistry,
	tags ...string,
) *Gradient2Limit {
	l, _ := NewGradient2Limit(
		name,
		20,
		200,
		20,
		func(limit int) int { return 4 },
		0.2,
		600,
		logger,
		registry,
		tags...,
	)
	return l
}
func NewGradient2Limit(
	name string,
	initialLimit int,  
	maxConurrency int,
	minLimit int,
	queueSizeFunc func(limit int) int,
	smoothing float64,
	longWindow int,
	logger Logger,
	registry core.MetricRegistry,
	tags ...string,
) (*Gradient2Limit, error) {
	if smoothing > 1.0 || smoothing < 0 {
		smoothing = 0.2
	}
	if maxConurrency <= 0 {
		maxConurrency = 1000
	}
	if minLimit <= 0 {
		minLimit = 4
	}
	if longWindow < 0 {
		longWindow = 100
	}
	if logger == nil {
		logger = NoopLimitLogger{}
	}
	if registry == nil {
		registry = core.EmptyMetricRegistryInstance
	}
	if minLimit > maxConurrency {
		return nil, fmt.Errorf("minLimit must be <= maxConcurrency")
	}
	if queueSizeFunc == nil {
		queueSizeFunc = func(limit int) int { return 4 }
	}
	if initialLimit <= 0 {
		initialLimit = 4
	}
	l := &Gradient2Limit{
		estimatedLimit:          float64(initialLimit),
		maxLimit:                maxConurrency,
		minLimit:                minLimit,
		queueSizeFunc:           queueSizeFunc,
		smoothing:               smoothing,
		shortRTT:                &measurements.SingleMeasurement{},
		longRTT:                 measurements.NewExponentialAverageMeasurement(longWindow, 10),
		longRTTSampleListener:   registry.RegisterDistribution(core.PrefixMetricWithName(core.MetricMinRTT, name), tags...),
		shortRTTSampleListener:  registry.RegisterDistribution(core.PrefixMetricWithName(core.MetricWindowMinRTT, name), tags...),
		queueSizeSampleListener: registry.RegisterDistribution(core.PrefixMetricWithName(core.MetricWindowQueueSize, name), tags...),
		listeners:               make([]core.LimitChangeListener, 0),
		logger:                  logger,
		registry:                registry,
	}
	l.commonSampler = core.NewCommonMetricSampler(registry, l, name, tags...)
	return l, nil
}
func (l *Gradient2Limit) EstimatedLimit() int {
	l.mu.RLock()
	defer l.mu.RUnlock()
	return int(l.estimatedLimit)
}