Есть ли в C # импульс boost :: shared_ptr <T>? - PullRequest
3 голосов
/ 17 января 2010

Просто любопытно, я использовал boost: shared_ptr в прошлом LOT - для того, чтобы несколько объектов хранили общий указатель на один объект и т. Д.

Есть ли эквивалент этой функциональности в C #?

Ответы [ 5 ]

9 голосов
/ 17 января 2010

boost :: shared_ptr позволяет указатели с подсчетом ссылок в среде, которая не является сборщиком мусора. Среда выполнения .NET обеспечивает полную сборку мусора, поэтому нет необходимости в контейнере указателя подсчета ссылок - просто сохраняйте ссылки на ваши объекты.

5 голосов
/ 28 февраля 2014

GC устраняет необходимость в общих указателях, когда дело доходит до управления памятью, но не делает этого для управления ресурсами.

IDisposable - это решение .NET для управления ресурсами, но оно не позволяетсемантика долевой собственности.См. в этой статье для подробного обсуждения слабых мест.

Вот простая реализация SharedRef, которая следует шаблону boost :: shared_ptr объекта выделенного количества ссылок в куче.Не уверен, насколько это будет полезно, но не стесняйтесь комментировать и улучшать его ...

/// <summary>
/// SharedRef class, which implements reference counted IDisposable ownership.
/// See also the static helper class for an easier construction syntax.
/// </summary>
public class SharedRef<T> : IDisposable
    where T:class,IDisposable
{
    private SharedRefCounter<T> _t;

    /// <summary>
    /// Create a SharedRef directly from an object. Only use this once per object.
    /// After that, create SharedRefs from previous SharedRefs.
    /// </summary>
    /// <param name="t"></param>
    public SharedRef(T t)
    {
        _t = new SharedRefCounter<T>(t);
        _t.Retain();
    }

    /// <summary>
    /// Create a SharedRef from a previous SharedRef, incrementing the reference count.
    /// </summary>
    /// <param name="o"></param>
    public SharedRef(SharedRef<T> o)
    {
        o._t.Retain();
        _t = o._t;
    }

    public static SharedRef<T> Create(T t)
    {
        return new SharedRef<T>(t);
    }

    private bool _disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            if (_t != null)
            {
                _t.Release();
                _t = null;
            }
        }

        _disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
    }

    public T Get()
    {
        return _t.Get();
    }
}

/// <summary>
/// Static helper class for easier construction syntax.
/// </summary>
public static class SharedRef
{
    /// <summary>
    /// Create a SharedRef directly from an object. Only use this once per object.
    /// After that, create SharedRefs from previous SharedRefs.
    /// </summary>
    /// <param name="t"></param>
    public static SharedRef<T> Create<T>(T t) where T : class,IDisposable
    {
        return new SharedRef<T>(t);
    }

    /// <summary>
    /// Create a SharedRef from a previous SharedRef, incrementing the reference count.
    /// </summary>
    /// <param name="o"></param>
    public static SharedRef<T> Create<T>(SharedRef<T> o) where T : class,IDisposable
    {
        return new SharedRef<T>(o);
    }
}

/// <summary>
/// Class which holds the reference count for a shared object.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class SharedRefCounter<T> where T : class,IDisposable
{
    private int _count;
    private readonly T _t;

    public T Get()
    {
        return _t;
    }

    public SharedRefCounter(T t)
    {
        _count = 0;
        _t = t;
    }

    /// <summary>
    /// Decrement the reference count, Dispose target if reaches 0
    /// </summary>
    public void Release()
    {
        lock (_t)
        {
            if (--_count == 0)
            {
                _t.Dispose();
            }
        }
    }

    /// <summary>
    /// Increment the reference count
    /// </summary>
    public void Retain()
    {
        lock (_t)
        {
            ++_count;
        }
    }
}

Примечания:

  1. Чтобы обеспечить только один счетчик ссылок для каждого общего ресурсаобъект, убедитесь, что вы только создаете 1 SharedRef непосредственно из объекта, а после этого создаете новые SharedRefs из предыдущих SharedRef.Это то же самое для boost :: shared_ptr.Было бы неплохо добавить некоторую защиту классу на случай, если вы об этом забудете.
  2. Кажется позором, что сам SharedRef должен быть ссылочным типом, размещенным в куче, но я думаю, что это единственныйспособ сделать его одноразовым.
  3. Я еще не реализовал эквивалент ослабленного_птр, но я думаю, что его можно легко добавить.
  4. Это потокобезопасно (я думаю), но, вероятно, может бытьболее эффективный, поскольку он использует блокировки.

Вот некоторый тестовый код, показывающий его в действии через 3 потока.

[TestFixture]
public class SharedRefTest
{
    public class MyDisposable : IDisposable
    {
        private bool _disposed = false;
        private string _id;

        public MyDisposable(string id) { _id = id; }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    Console.WriteLine("{0}: Disposing {1}", Thread.CurrentThread.ManagedThreadId, _id);
                }

                _disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        public override string ToString()
        {
            return _id;
        }
    }

    [Test]
    public void Run()
    {
        Task t1, t2, t3;

        // create 2 objects
        Console.WriteLine("{0}: starting initial scope", Thread.CurrentThread.ManagedThreadId);
        using (var o1 = SharedRef.Create(new MyDisposable("o1")))
        using (var o2 = SharedRef.Create(new MyDisposable("o2")))
        {
            // and 3 sharedrefs, which will be Disposed in 3 separate threads
            var p1 = SharedRef.Create(o1);
            var p2a = SharedRef.Create(o2);
            var p2b = SharedRef.Create(o2);
            t1 = Task.Run(() =>
            {
                using (p1)
                {
                    Console.WriteLine("{0}: in another thread, using o1", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(1000);
                    Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId);
                }
            });
            t2 = Task.Run(() =>
            {
                using (p2a)
                {
                    Console.WriteLine("{0}: in another thread, using o2", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(1000);
                    Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId);
                }
            });
            t3 = Task.Run(() =>
            {
                using (p2b)
                {
                    Console.WriteLine("{0}: in another thread, using o2", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(1000);
                    Console.WriteLine("{0}: in another thread, exiting scope", Thread.CurrentThread.ManagedThreadId);
                }
            });
            Console.WriteLine("{0}: exiting initial scope", Thread.CurrentThread.ManagedThreadId);
        }
        t1.Wait();
        t2.Wait();
        t3.Wait();
    }
}
5 голосов
/ 17 января 2010

Нет необходимости. .NET имеет сборщик мусора, который позаботится об очистке объекта, если на него ничего не ссылается.

1 голос
/ 17 января 2010

Прочитайте эту статью для получения дополнительной информации:

0 голосов
/ 17 января 2010

Существует несколько стратегий автоматического управления памятью на разных платформах и языках программирования.

Два основных подхода применимы к автоматическому управлению памятью: ссылка подсчет и сборка мусора. Они оба заслуживают изучения, хотя второй на сегодняшний день является более мощным и общеприменимым.

(Построение объектно-ориентированного программного обеспечения Бертрана Мейера, с.301)

Подсчет ссылок (то есть shared_ptr) - один из самых простых способов автоматического управления памятью. Это довольно просто, но имеет некоторые существенные недостатки (он не может иметь дело с циклическими структурами, и у него есть издержки производительности как во времени, так и в пространстве. Для каждой операции над ссылками реализация теперь будет выполнять арифметическую операцию - и, в случае отсоединения, условная инструкция. Кроме того, каждый объект должен быть расширен дополнительным полем для хранения счетчика).

Идея первой технологии автоматического управления памятью, подсчета ссылок, это просто. В каждом объекте мы храним количество ссылок на объект; когда этот счет становится нулевым, объект может быть переработан. Это решение не сложно реализовать (на уровне реализации языка). Мы должен обновить счетчик ссылок любого объекта в ответ на все операции, которые могут создать объект, присоедините новую ссылку к нему и отсоедините ссылку от него.

(Построение объектно-ориентированного программного обеспечения Бертрана Мейера, с.301)

Сборка мусора (ведьма используется в CLR) основана на двух основных свойствах:

Надежность : каждый собранный объект недоступен.

Полнота : будет собран каждый недоступный объект.

Основа сбора мусора

Базовый алгоритм обычно включает две фазы, по крайней мере, концептуально: отметка и подметать. Фаза марки, начиная с начала координат, рекурсивно следует ссылкам на пройти активную часть конструкции, пометив как достижимые все объекты, с которыми он сталкивается. Фаза развертки пересекает всю структуру памяти, восстанавливая немаркированные элементы и разметить все. Как и при подсчете ссылок, объекты должны включать дополнительное поле, используемое здесь для маркировка; но затраты пространства незначительны, так как для каждого объекта достаточно одного бита. Как будет видно при изучении динамического связывания, реализация средств O-O требует, чтобы каждый Объект несет некоторую дополнительную внутреннюю информацию (например, его тип) в дополнение к его официальной поля, соответствующие атрибутам генерирующего класса. Эта информация обычно занимает одно или два слова на объект; маркировочный бит обычно можно сжать в один из эти дополнительные слова, так что на практике нет заметных накладных расходов.

P.S. Для получения дополнительной информации о сборке мусора в CLR см. Главу 20 «CLR через C #» Джеффри Рихтера

P.S.S. Для shared_ptr в .Net нет эквивалента, потому что .Net использует сборщик мусора для автоматического управления памятью, а если вы хотите использовать подсчет ссылок - вам следует реализовать его вручную (например, для управления ресурсами).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...