Esecuzione differita di Dispose per oggetti IDisposable

4

Sto lavorando a un'applicazione che esegue molte operazioni di crittografia e decrittografia nell'applicazione e questo è probabilmente il collo di bottiglia numero uno, quindi ho impiegato del tempo per apportare modifiche alle prestazioni. Un sacco di questo ha coinvolto semplicemente il caching delle cose in memoria (mi rendo conto che c'è qualcosa di un compromesso nella sicurezza nel farlo), ma ho notato durante il profiling che Dispose () era una buona parte del tempo a decodificare (credo per .NET la crittografia riempie tutto di zero, quindi questo ha senso). Quindi mi è venuta in mente questa idea:

Avere un "deposito di riserva". Invece di usare blocchi, creare oggetti, usarli, restituire il risultato e aggiungerli al pool di smaltimento nel blocco finally. Internamente, il pool di smaltimento utilizza una coda e un timer e ogni volta che il timer lo attiva, deseleziona gli oggetti e li elimina.

Ho provato a implementarlo e sembra funzionare e migliorare le prestazioni, ma ripeto, la creazione di profili a livello locale non è un caso di utilizzo davvero realistico. Questo suono? Sono probabile che incappi in problemi di prestazioni in fuga a cui non sto pensando attualmente?

Suppongo che dovrei aggiungere che si tratta di un'applicazione ASP.NET MVC, quindi tutto ruota intorno alle richieste.

    
posta Casey 21.07.2014 - 17:31
fonte

2 risposte

1

Potresti dare un'occhiata a ex 28v = vs.103%29.aspx"> di Microsoft. ScheduledDisposable. Non l'ho mai usato, ma sembra che metterà in coda i tuoi oggetti per essere eliminati su un thread separato.

Ma se un pool è ciò che stai cercando, penso che funzionerà:

public interface IDisposableWrapper<TDisposable> : IDisposable where TDisposable : class, IDisposable
{
    TDisposable Reference { get; }
}

public interface IDisposableWrapperFactory<TDisposable> where TDisposable : class, IDisposable
{
    IDisposableWrapper<TDisposable> Create();
}

public sealed class ReusableDisposableFactory<TDisposable> : IDisposableWrapperFactory<TDisposable>, IDisposable
    where TDisposable : class, IDisposable
{
    readonly object padlock = new object();
    Func<TDisposable> getReference;
    Stack<TDisposable> stack;
    int capacity;

    public ReusableDisposableFactory(Func<TDisposable> getReference, int capacity)
    {
        if (getReference == null)
            throw new ArgumentNullException("getReference");
        this.stack = new Stack<TDisposable>(capacity);
        this.capacity = capacity;
        this.getReference = getReference;
    }

    bool IsDisposed { get { return stack == null; } }

    void ThrowOnDisposed()
    {
        if (IsDisposed)
            throw new ObjectDisposedException(GetType().Name);
    }

    sealed class ReusableDisposableWrapper : IDisposableWrapper<TDisposable>
    {
        ReusableDisposableFactory<TDisposable> factory;
        TDisposable reference;

        internal ReusableDisposableWrapper(ReusableDisposableFactory<TDisposable> factory, TDisposable reference)
        {
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.factory = factory;
            this.reference = reference;
        }

        public bool IsDisposed { get { return reference == null; } }

        #region IDisposableWrapper<TDisposable> Members

        public TDisposable Reference
        {
            get { return reference; }
            private set { reference = value; }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // Dispose of unmanaged resources.
            Dispose(true);
            // Suppress finalization.  Since this class actually has no finalizer, this does nothing.
            GC.SuppressFinalize(this);
        }

        void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Free any other managed objects here.
                var reference = Interlocked.Exchange(ref this.reference, null);
                if (reference != null)
                    factory.DisposeReference(reference);
            }
            // Free any unmanaged objects here. 
        }

        #endregion

        public override string ToString()
        {
            var theReference = Reference;
            if (IsDisposed || theReference == null)
                return base.ToString() + ": Disposed";
            else
                return base.ToString() + ": " + theReference.ToString();
        }
    }

    #region IDisposableWrapperFactory<TDisposable> Members

    public IDisposableWrapper<TDisposable> Create()
    {
        lock (padlock)
        {
            ThrowOnDisposed();
            TDisposable reference;
            if (stack.Count > 0)
            {
                reference = stack.Pop();
            }
            else
            {
                reference = getReference();
            }
            return new ReusableDisposableWrapper(this, reference);
        }
    }

    void DisposeReference(TDisposable reference)
    {
        lock (padlock)
        {
            if (reference == null)
                return;
            ThrowOnDisposed();
            if (stack.Count < capacity)
            {
                stack.Push(reference);
            }
            else
            {
                reference.Dispose();
            }
        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        // Dispose of unmanaged resources.
        Dispose(true);
        // Suppress finalization.  Since this class actually has no finalizer, this does nothing.
        GC.SuppressFinalize(this);
    }

    void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (padlock)
            {
                if (!IsDisposed)
                {
                    while (stack.Count > 0)
                    {
                        var reference = stack.Pop();
                        reference.Dispose();
                    }
                    stack = null;
                    getReference = null;
                }
            }
        }
        // Free any unmanaged objects here. 
    }

    #endregion

    public override string ToString()
    {
        string str = base.ToString();
        if (!Monitor.TryEnter(padlock))
        {
            // Don't block for ToString()
            str = str + ", locked.";
        }
        else
        {
            try
            {
                if (IsDisposed)
                    str = str + ", Disposed";
                else
                    str = string.Format("{0}: {1} {2} cached", str, stack.Count, typeof(TDisposable).Name);
            }
            finally
            {
                Monitor.Exit(padlock);
            }
        }
        return str;
    }
}

Nota che la fabbrica stessa è usa e getta. Certamente sarei riluttante a usare questo per oggetti finalizzabili comunque.

    
risposta data 26.07.2014 - 02:04
fonte
1

forse vuoi un raccoglitore che gira regolarmente e quando lo fa, pulisce questi .. come possiamo chiamarli .. Lo so - oggetti 'garbage'. Possiamo chiamare questa cosa un netturbino.

È stato aggiunto Dispose per fornire la finalizzazione deterministica in modo che gli oggetti possano essere puliti immediatamente piuttosto che attendere in un secondo momento, rendendo il Dispose non deterministico solo ridondante. Non è necessario reinventare la ruota.

Quindi, vuoi la finalizzazione che si verifica in un secondo momento, quindi usa solo un finalizzatore. Il GC funzionerà quando il carico è inferiore. È probabile che tu non voglia ciò, poiché vuoi che i tuoi oggetti morti vengano cancellati dalla memoria immediatamente, per evitare il rischio per la sicurezza di qualcosa che legge i contenuti in un secondo momento, probabilmente importante in un'applicazione di sicurezza.

Le prestazioni saranno le stesse nel complesso: stai semplicemente spostando il punto in cui la pulizia avviene da "adesso" a "un po 'più tardi". Di solito questo significa che l'utente non noterà il tempo trascorso a pulire, ma in un sistema molto usato non c'è modo di evitarlo.

    
risposta data 26.07.2014 - 15:44
fonte

Leggi altre domande sui tag