Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 08:02
Оценка:
Класс SomeTask может быть в двух состояниях:

— простаивает (можно изменять любые свойства, косяки с многопоточными обновлениями — проблемы вызывающего кода)

— работает (любые попытки изменить свойства/повторно запустить задачу, даже из другого потока — InvalidOperationException).

Чугуниевый прототип:
    public class SomeTask
    {
      #region Fields
      private readonly object lockKey = new object();

      private volatile bool isBusy;

      private string someOption;
      #endregion

      #region Public properties
      public bool IsBusy
      {
        get
        {
          return isBusy;
        }
      }

      public string SomeOption
      {
        get
        {
          return someOption;
        }
        set
        {
          ValidateNotBusy();
          someOption = value;
        }
      }
      #endregion

      #region Core logic
      private void ValidateNotBusy()
      {
        lock (lockKey)
        {
          if (isBusy)
          {
            throw new InvalidOperationException("blablabla");
          }
        }
      }

      public void Run()
      {
        bool run = false;

        try
        {
          lock (lockKey)
          {
            if (!isBusy)
            {
              isBusy = true;
              run = true;
            }
          }

          if (run)
          {
            LongRunningMethod();
          }
          else
          {
            throw new InvalidOperationException("blablabla");
          }
        }
        finally
        {
          if (run)
          {
            isBusy = false;
          }
        }
      }

      private void LongRunningMethod()
      {
        // ...
        if (someOption.Length == 42)
        {
          // ...
        }
        // ...
      }
      #endregion
    }


Как бы сделали вы?
Re: Как бы вы написали freezable-класс?
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 18.11.10 08:54
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Класс SomeTask может быть в двух состояниях:


S>- простаивает (можно изменять любые свойства, косяки с многопоточными обновлениями — проблемы вызывающего кода)


S>- работает (любые попытки изменить свойства/повторно запустить задачу, даже из другого потока — InvalidOperationException).


S>Чугуниевый прототип:

S>
.....
S>


S>Как бы сделали вы?



Так же сделал бы, чем приведенный код самому не нравится?
Re: Как бы вы написали freezable-класс?
От: Аноним  
Дата: 18.11.10 09:01
Оценка: +1
Здравствуйте, Sinix, Вы писали:

скажу не потеме

S>Чугуниевый прототип:

S>
S>    public class SomeTask
S>    {
S>      private volatile bool isBusy;

S>      private void ValidateNotBusy()
S>      {
S>        lock (lockKey)
S>        {
S>          if (isBusy)
S>          {
S>            throw new InvalidOperationException("blablabla");
S>          }
S>        }
S>      }
S>


S>Как бы сделали вы?

выкинул ms выделенную блокировку, есть в аругментах InvalidOperationException не используются члены класса требующие доступа с блокировкой
Re[2]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 09:02
Оценка:
Здравствуйте, achmed, Вы писали:

A>Так же сделал бы, чем приведенный код самому не нравится?


А вот хз... корявый он какой-то: смесь lock с volatile конечно работает, но выглядит странно Как вариант, можно заменить доступ к полю на Interlocked.*, но здесь это будет выглядеть скорее понтами, чем обоснованным решением. А, ну да, ещё можно извратиться с RWLockSlim

Просто подозреваю что кто-то подобную задачу решал и придумал решение посимпатичней
Re: Как бы вы написали freezable-класс?
От: Jolly Roger  
Дата: 18.11.10 09:51
Оценка:
Здравствуйте, Sinix, Вы писали:

По-моему, у Вас не разруливается ситуация, в которой Run вызывается в момент, когда кто-то находится внутри сеттера. Если это возлагается на внешний код, то и lock для доступа к isBusy вроде как не нужен. Ну а так, я-бы скорее всего на Interlocked ориентировался.
"Нормальные герои всегда идут в обход!"
Re: Как бы вы написали freezable-класс?
От: _FRED_ Черногория
Дата: 18.11.10 09:59
Оценка: 18 (1)
Здравствуйте, Sinix, Вы писали:

S>- простаивает (можно изменять любые свойства, косяки с многопоточными обновлениями — проблемы вызывающего кода)

S>- работает (любые попытки изменить свойства/повторно запустить задачу, даже из другого потока — InvalidOperationException).

S>Как бы сделали вы?


Свойства — по аналогии с DependencyProperty, только не статическими, а экземплярными.

Без секции, к сожелению, никак: надо же сначала проверить, а затем изменить значение.
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 10:00
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>По-моему, у Вас не разруливается ситуация, в которой Run вызывается в момент, когда кто-то находится внутри сеттера.

Да, именно так, щас пытаюсь сообразить, сильно ли это критично. Пока выходит, что нет.
Re[3]: Как бы вы написали freezable-класс?
От: Jolly Roger  
Дата: 18.11.10 10:09
Оценка: 18 (1)
Здравствуйте, Sinix, Вы писали:

S>Да, именно так, щас пытаюсь сообразить, сильно ли это критично. Пока выходит, что нет.


Мне кажется это странным Вот смотрите, кто-то входит в сеттер, успешно проходит ValidateNotBusy() и сразу после неё планировщик отбирает у этого потока управление. Далее другой поток вызывает Run и уходит в недра LongRunningMethod(). В какой-то момент первый поток опять получает доступ к процессору и изменяет поле. По-моему, это ничем не отличается от вызова сеттера во время работы Run, от чего Вы вроде как пытаетесь защититься
"Нормальные герои всегда идут в обход!"
Re: Как бы вы написали freezable-класс?
От: Clickmaker Россия http://relaxander.webest.net/
Дата: 18.11.10 10:13
Оценка:
public string SomeOption
{
 get { return someOption; }
 set
 {
  if (!isBusy)
  {
   lock(lockKey)
   {
    if (!isBusy)
    {
     someOption = value;
    }
   }
  }
 }
}
Re[4]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 10:32
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Мне кажется это странным


Ну да, криво, надо оборачивать в lock весь setter (_Fred_ выше тоже предложил аналогичное решение).
Класс действительно внутренний и его инициализацию и запуск задач разделяет большой промежуток времени. Но проще сразу сделать хорошо, чем потом
Re[5]: Как бы вы написали freezable-класс?
От: Jolly Roger  
Дата: 18.11.10 10:46
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Ну да, криво, надо оборачивать в lock весь setter (_Fred_ выше тоже предложил аналогичное решение).


Ну да, вобщем-то. Плохо только, что это потребует одного локера на все свойства, что плохо скажется на параллелизме. Я-бы всё-таки рассмотрел использование RWLockSlim или собственной похожей реализации, заточенной под данную ситуацию, заодно обдумав желательность и возможность снять с внешнего кода ответственность за синхронизацию доступа к свойствам.
"Нормальные герои всегда идут в обход!"
Re[6]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 11:16
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Ну да, вобщем-то. Плохо только, что это потребует одного локера на все свойства, что плохо скажется на параллелизме.


Ну да. Код давно жил, потом как всегда внезапно появилась многопоточность и тут же под неё приходится наворачивать инфраструктуру
Re: Как бы вы написали freezable-класс?
От: hardcase Пират http://nemerle.org
Дата: 18.11.10 11:26
Оценка: 26 (3) +1
Здравствуйте, Sinix, Вы писали:

S>Класс SomeTask может быть в двух состояниях:


S>- простаивает (можно изменять любые свойства, косяки с многопоточными обновлениями — проблемы вызывающего кода)


S>- работает (любые попытки изменить свойства/повторно запустить задачу, даже из другого потока — InvalidOperationException).


Паттерн автомат состояний?


    public class SomeTask
    {
        public SomeTask()
        {
            this.state = new SettingState();
        }
    
      private volatile ITaskBackend state;

      #region Public properties
      public bool IsBusy
      {
        get
        {
            return state.IsRunning;
        }
      }

      public string SomeOption
      {
        get
        {
          return state.SomeOption;
        }
        set
        {
          state.SomeOption = value;
        }
      }
      #endregion

      #region Core logic
      public void Run()
      {
        state = state.BeginRun();
        try 
        {
            LongRunningMethod();
        }
        finally 
        {
            state = state.EndRun();
        }
      }
      #endregion
    }
    
    interface ITaskBackend
    {
        string SomeOption { get; set; }
        bool IsRunning { get; }
        ITaskBackend BeginRun();
        ITaskBackend EndRun();
    }

    class SettingState : ITaskBackend
    {
        public string SomeOption { get; set; }
        public bool IsRunning { get { return false; } }
        public ITaskBackend BeginRun()
        {
            return new RunningState(SomeOption);
        }
        
        public ITaskBackend EndRun()
        {
            throw new InvalidOperationException();
        }
    }

    class RunningState : ITaskBackend
    {
        public RunningState(string someOption)
        {
            this.someOption = someOption;
        }
    
        private readonly string someOption;
        public string SomeOption {
            get { return someOption; }
            set { throw new InvalidOperationException(); }
        }
        
        public bool IsRunning { get { return true; } }
        
        public ITaskBackend BeginRun()
        {
            throw new InvalidOperationException();
        }
        
        public ITaskBackend EndRun()
        {
            return new SomeOption();
        }
    }
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 11:32
Оценка:
Здравствуйте, hardcase, Вы писали:

Вах, я ведь такой подход видел — его Transaction внутри себя использует. Весьма красиво
Re[3]: Как бы вы написали freezable-класс?
От: hardcase Пират http://nemerle.org
Дата: 18.11.10 11:35
Оценка: +2
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, hardcase, Вы писали:


S>Вах, я ведь такой подход видел — его Transaction внутри себя использует. Весьма красиво


Вообще синхронизация все же нужна, но только в одном месте:
lock(lockKey)
 state = state.BeginRun();
/* иЗвиНите зА неРовнЫй поЧерК */
Re: Как бы вы написали freezable-класс?
От: Wolverrum Ниоткуда  
Дата: 18.11.10 12:40
Оценка: 15 (2)
Вау, автоматы, свойства, чугуний...

Внесу свою архинеизящную лепту

interface IFreezable // чисто для наглядности
{
    bool IsFreeze{get;set;}
}

[Serializable]
[AttributeUsage(AttributeTargets.Class)]
public sealed class CanFreezeAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if(args.Instance is IFreezable) // Мало ли, найдется личность...
        {
            if(( args.Instance as IFreezable ).IsFreeze)
                throw new AccessViolationException();
            else
                args.FlowBehavior = FlowBehavior.Continue;
        }
        else
            throw new AccessViolationException();
    }
}

[CanFreeze] // вуаля
class X : IFreezable
{
    public bool IsFreeze{get;set;}

    public void Test()
    {
        Console.WriteLine("Test");
    }
}
Re[2]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 13:00
Оценка:
Здравствуйте, Wolverrum, Вы писали:

W>Внесу свою архинеизящную лепту


Постшарп небось?
Re[3]: Как бы вы написали freezable-класс?
От: Wolverrum Ниоткуда  
Дата: 18.11.10 13:09
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Wolverrum, Вы писали:


W>>Внесу свою архинеизящную лепту


S>Постшарп небось?


Ато!

Хотя ТС лев (неправ) архитектурно, имхо, если уж морозиться в рамках ООП, то к объекту следовало бы иметь строго одну точку доступа, которая предоставляла бы интерфейс для работы с объектом. Или не предоставляла — это уже по ситуации
Re[4]: Как бы вы написали freezable-класс?
От: Wolverrum Ниоткуда  
Дата: 18.11.10 13:16
Оценка: +1
Здравствуйте, Wolverrum, Вы писали:
W> иметь строго одну точку доступа
Ну это, похоже, и есть тот самый "паттерн автомат состояний"
Re[4]: Как бы вы написали freezable-класс?
От: Sinix  
Дата: 18.11.10 15:39
Оценка:
Здравствуйте, Wolverrum, Вы писали:


W>Хотя ТС лев (неправ) архитектурно, имхо, если уж морозиться в рамках ООП, то к объекту следовало бы иметь строго одну точку доступа, которая предоставляла бы интерфейс для работы с объектом. Или не предоставляла — это уже по ситуации


ТС — это я И да, идея с паттерном состояния мне пока что нравится больше всего. Поскольку у базового класса туча наследников, может проще окажется _сначала_ переизобрести dependency properties, а _затем_ — реализовать вариант hardcase. В результате получится как раз Freezable из WPF
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.