@kttotto
все, что .NET

Есть ли такой реализованный список на C#?

Для задачи необходим список, который генерировал бы событие при появлении в нем записи и в котором можно указать время жизни записи. Ну с генерацией события о записи легче, такое есть, а вот с указанием времени сколько эта запись должна там пробыть - не нашел. Может кто знает, есть ли готовые решения или придется руками решать?
  • Вопрос задан
  • 400 просмотров
Решения вопроса 2
@kttotto Автор вопроса
все, что .NET
Всем спасибо, реализовал руками на следующий день. Не закрывал вопрос, что может кто-то все-таки даст готовый вариант. И да, многопоточность там имеет значение, и скорость реагирования тоже.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Ogoun
@Ogoun
Programmer
Для решения подобной задачи я использовал свое решение, где делал односвязный список объектов, обертывающих мой объект и добавляющих поле с временем удаления. Класс со списком объектов содержит один таймер, из Threading. При добавлении нового объекта он вставляется с сортировкой по дате, т.е. первый объект в списке всегда с самым коротким временем жизни, при добавлении/удалении перерасчитывается таймер. В итоге ресурсов потребляется мало, но сложность вставки/удаления O(N)

Вот упрощенный рабочий пример:
internal class TemporaryObject
{
    private static long _counter = 0;
    public long Key { get; private set; }
    public TemporaryObject()
    {
        Key = Interlocked.Increment(ref _counter);
    }
    /// <summary>
    /// Событие при завершении ожидания
    /// </summary>
    public Action Callback;
    /// <summary>
    /// Срок истечения ожидания
    /// </summary>
    public DateTime ExpirationDate;
    /// <summary>
    /// Следующий объект с ближайшей датой окончания ожидания
    /// </summary>
    public TemporaryObject Next;
}

public class TemporaryObjectPool
{
    private readonly object _locker = new object();
    /// <summary>
    /// Таймер. Один на всех
    /// </summary>
    private Timer _timer;
    /// <summary>
    /// Объект с ближайшей датой окончания ожидания
    /// </summary>
    private TemporaryObject _current = null;
    /// <summary>
    /// Переустановка таймера
    /// </summary>
    private void ResetTimer()
    {
        if (null != _current)
        {
            var diff = (_current.ExpirationDate - DateTime.Now).TotalMilliseconds;
            if (diff < 0) diff = 0;
            _timer.Change((int)diff, Timeout.Infinite);
        }
        else
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
        }
    }

    public TemporaryObjectPool()
    {
        _timer = new Timer(state =>
        {
            Action action = null;
            lock (_locker)
            {
                if (null != _current)
                {
                    // Получаем событие для исполнения
                    action = _current.Callback;
                    // Находим следующий ожидающий объект
                    _current = _current.Next;
                    // Перезадание таймера
                    ResetTimer();
                }
            }
            // Вызов события ожидавшего даты
            if (null != action)
            {
                ThreadPool.QueueUserWorkItem(s => action());
            }
        }, null, Timeout.Infinite, Timeout.Infinite);
    }

    /// <summary>
    /// Добавление ожидающего объекта
    /// </summary>
    /// <param name="insert"></param>
    internal long Push(TemporaryObject insert)
    {
        lock (_locker)
        {
            // Если пул пуст, то добавляемое событие становится корневым
            if (null == _current)
            {
                _current = insert;
            }
            else
            {
                // Если пул не пуст
                var cursor = _current;
                TemporaryObject prev = null;
                // Поиск места для вставки, сложность вставки O(n) в худшем случае
                do
                {
                    if (DateTime.Compare(cursor.ExpirationDate, insert.ExpirationDate) > 0)
                    {
                        insert.Next = cursor;
                        if (null == prev)
                        {
                            _current = insert;
                        }
                        else
                        {
                            prev.Next = insert;
                        }
                        break;
                    }
                    prev = cursor;
                    cursor = cursor.Next;
                    if (cursor == null)
                    {
                        prev.Next = insert;
                    }
                } while (cursor != null);
            }
            ResetTimer();
        }
        return insert.Key;
    }

    public void Remove(long key)
    {
        lock (_locker)
        {
            if (_current == null) return;
            bool removed = false;
            if (_current.Key == key)
            {
                _current = _current.Next;
                removed = true;
            }
            else
            {
                var prev = _current;
                var next = _current.Next;
                while (next != null)
                {
                    if (next.Key == key)
                    {
                        prev.Next = next.Next;
                        removed = true;
                        break;
                    }
                    prev = next;
                    next = next.Next;
                }
            }
            if (removed)
            {
                ResetTimer();
            }
        }
    }
}


И использование
var pool = new TemporaryObjectPool();
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#1 removed"), ExpirationDate = DateTime.Now.AddSeconds(5) });
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#2 removed"), ExpirationDate = DateTime.Now.AddSeconds(10) });
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#3 removed"), ExpirationDate = DateTime.Now.AddSeconds(15) });
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы