Re[6]: lock (this)
От: Kluev  
Дата: 29.03.06 11:31
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Сергей Губанов wrote:

>> 1) *Все* данные инкапсулированы внутри объектов.
>> 2) Доступ к данным осуществляется *только* с помощью методов объекта.
>> 3) Для синхронизированного (эксклюзивного) доступа к данным используются
>> *эксклюзивные методы*.
C>Вы только что описали аппартаментную (appartament) потоковую модель COM.
C>Рассказать какие с ней проблемы возникают?

Какие?
Re: lock (this)
От: alexeiz  
Дата: 29.03.06 11:41
Оценка:
СГ>Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>>Знаешь, что такое Очень Плохая Вещь?

ГВ>>Это вот она:
СГ>>>C#:
СГ>
СГ>  System.Monitor.Enter(this);
СГ>


СГ>Здравствуйте, Дарней, Вы писали:


Д>> Вот придёт кому-то в голову синхронизироваться на этом же объекте, но в другом месте и в других целях — и всё, приплыли.


А это, кстати, интересный вопрос. Вот в .NET у каждого объекта сделали sync block, чтобы каждому объекту можно было сделать lock(this). А потом выяснилось, что это очень и очень плохо делать lock(this). Начали рекомендовать практику введения отдельного синхронизирующего объекта, т.е.
class MySyncClass
{
    object sync_obj = new object();
    public void foo()
    {
        lock (sync_obj)
        {
            // modify me
        }
    }
}

Спрашивается, зачем у каждого объекта sync block, если он в подавляющем большинстве случаев не используется, а используем мы для синхронизации другой, специально созданный для этого объект, для которого можно было бы иметь специальный класс. И только этот класса должен иметь sync block. У остальных классов от sync block'а можно было бы избавиться. А sync block между прочим 4 байта требует.
Re[3]: Решение задачи об очереди
От: GlebZ Россия  
Дата: 29.03.06 12:17
Оценка: +1
Здравствуйте, Сергей Губанов, Вы писали:

УжасЪ
Сергей, предлагаю вам в качестве домашнего задания решить данную задачу вообще без объектов синхронизации. Максимум, InterlockedExchange, чтобы вам полегче было.
Re[3]: Решение задачи об очереди
От: minorlogic Украина  
Дата: 29.03.06 13:00
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Вот...


А в Dequeue () разве мы не поймали дедлок ?

Как кто то может что то добавить , если мы залочили все выполнение ? Или я чет в механизмах шарповских не понимаю
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[4]: Monitor
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 29.03.06 13:47
Оценка:
Здравствуйте, minorlogic, Вы писали:

M>А в Dequeue () разве мы не поймали дедлок ?


M>Как кто то может что то добавить , если мы залочили все выполнение ? Или я чет в механизмах шарповских не понимаю


Это не шарповские механизмы, и даже не дотнетные, это называется Монитор.

Шарповский lock (obj) {...} — это синтаксический сахар над:
System.Threading.Monitor.Enter(obj)
try
{
  ...эксклюзивный блок кода
}
finally
{
  System.Threading.Monitor.Exit(obj);
}

У монитора, кроме Enter(obj) и Exit(obj) есть еще Wait(obj) и PulseAll(obj), которые можно вызывать только находясь внутри эксклюзивного блока кода. Wait — усыпляет поток до тех пор пока кто-то другой не вызовет PulseAll. Уснувший по Wait поток считается (временно) покинувшим эксклюзивный блок, так что эксклюзивный блок становится вновь доступен для проникновения в него другого потока, поработав внутри эксклюзивного блока другой поток, уходя, должен вызвать PulseAll для того чтобы разбудить уснувшие по Wait другие потоки. Итак, на вход в эксклюзивный блок есть две очереди. Одна очередь образуется на барьере Enter, а другая очередь — это уже вошедшие ранее в эксклюзивный блок потоки, но уснувшие внутри него по Wait.
        public object Dequeue ()
        {
            // перед этой командой потоки ставятся в очередь: внутрь вход по одному!
            lock (this)
            {
                // Проникнув внутрь эксклюзивного блока, поток проверяет очередь на непустоту,
                // если она пуста - засыпает. Как только он засыпает по Wait, то объект this становится
                // вновь доступен для других потоков. Раз так, то какой-то другой поток может вызвать
                // метод Enqueue и сделать очередь непустой. В конце метода Enqueue зовётся PulseAll.
                // Сработавший PulseAll будит уснувшие на этом объекте потоки. Разбуженные потоки вновь
                // впускаются внутрь эксклюзивного блока (по одному), 
                // из которого они были временно выведены на время своего сна
                while (this.head == null) System.Threading.Monitor.Wait(this);

                object r = this.head.element;
                this.head = this.head.next;
                if (this.head == null) this.tail = null;
                System.Threading.Monitor.PulseAll(this);
                return r;
            }
        }


На мониторах построен язык Active Oberon:

Active Oberon Language Report: http://bluebottle.ethz.ch/languagereport/index.html


В этом языке Monitor.Enter/Exit — обозначаются блоком

BEGIN {EXCLUSIVE}
...
END;

PulseAll — подразумевается перед выходом из каждого {EXCLUSIVE} блока.

Monitor.Wait обозначается инструкцией AWAIT (condition)

Итого, для синхронизации, в язык введено два дополнительных служебных слова EXCLUSIVE и AWAIT — с помощью них реализуются все механизмы синхронизации:
Synchonization Examples: http://bluebottle.ethz.ch/languagereport/node8.html


Вот аналог того буфера:
MODULE Buffers;

CONST
  BufLen = 256;
  
TYPE
  (* Buffer- First-in first-out buffer *)

  Buffer* = OBJECT
    VAR
      data: ARRAY BufLen OF INTEGER;
      in, out: LONGINT;
      
    (* Put - insert element into the buffer *)
    
    PROCEDURE Put* (i: INTEGER);
    BEGIN {EXCLUSIVE}
      AWAIT ((in + 1) MOD BufLen # out);  (*AWAIT ~full *)
      data[in] := i;
      in := (in + 1) MOD BufLen
    END Put;
    
    (* Get - get element from the buffer *)
    
    PROCEDURE Get* (VAR i: INTEGER);
    BEGIN {EXCLUSIVE}
      AWAIT (in # out);  (*AWAIT ~empty *)
      i := data[out];
      out := (out + 1) MOD BufLen
    END Get;
    
    PROCEDURE & Init;
    BEGIN
      in := 0; out := 0;
    END Init;
  
  END Buffer;

END Buffers.
Re[4]: Решение задачи об очереди
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 29.03.06 13:54
Оценка: :)
Здравствуйте, GlebZ, Вы писали:

GZ>Сергей, предлагаю вам в качестве домашнего задания решить данную задачу вообще без объектов синхронизации. Максимум, InterlockedExchange, чтобы вам полегче было.


На InterlockedExchange и, тем более, вообще без объектов синхронизации эту задачу решить нельзя. Какую-то другую — можно, эту — нет.
Re[6]: Решение задачи об очереди
От: ie Россия http://ziez.blogspot.com/
Дата: 29.03.06 14:16
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>>
СГ>>  bool IsAvailable {get;} // не пуста ли очередь?
СГ>>


СГ>Кстати, проверять пуста очередь или не пуста перед выполнением Dequeue — не очень осмысленное занятие, ведь в промежуток времени между вызовами IsAvailable и Dequeue очередь может успеть опустошить кто-то другой...


В том то и беда. Что вызов Dequeue может привести к бесконечному ожиданию, что на самом деле не допустимо. Решение разработчиков стандартной (System.Collection.Queue) очереди мне кажется более логичным. Но как я помню из ваших предыдущих постов, бросать исключение плохо, поэтому даже не знаю получится ли у вас красивое разрешение этой ситуации.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Re[5]: Monitor
От: minorlogic Украина  
Дата: 29.03.06 14:46
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Это не шарповские механизмы, и даже не дотнетные, это называется Монитор.


[skiped]

Спасибо , тогда более не менеее понятно! Чувствую надо будет ознакомиться
Ищу работу, 3D, SLAM, computer graphics/vision.
Re[7]: Решение задачи об очереди
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 29.03.06 14:47
Оценка:
Здравствуйте, ie, Вы писали:

ie>В том то и беда. Что вызов Dequeue может привести к бесконечному ожиданию, что на самом деле не допустимо.


В некоторых задачах очень даже допустимо, даже необходимо по логике работы. Например в моей программе есть такое место, когда распознаватель речи обратившись за очередной порцией данных отправляется в спячку если эта порция данных ещё не была к этому времени получена. Как только она получается, он автоматически будится просто от того, что в очередь произведена запись.

ie>Решение разработчиков стандартной (System.Collection.Queue) очереди мне кажется более логичным. Но как я помню из ваших предыдущих постов, бросать исключение плохо, поэтому даже не знаю получится ли у вас красивое разрешение этой ситуации.


Не блокирующий вызов не кидающий исключений в случае пустой очереди? Да элементарно:
bool Dequeue (out object obj);



if (queue.Dequeue(out x))
{
  ...
}
else
{
  ... очередь оказалась пустой
}
Re[7]: lock (this)
От: Cyberax Марс  
Дата: 29.03.06 15:57
Оценка:
Kluev wrote:
> C>Вы только что описали аппартаментную (appartament) потоковую модель COM.
> C>Рассказать какие с ней проблемы возникают?
> Какие?
1. Межпоточный маршаллинг — с языковой поддержкой он, конечно, будет
прозрачным и более эффективным цикла сообщений в Винде. Но все же...

2. Интересные проблемы со сроками жизни аппартамента и ссылок на его
объекты в других потоках. Хорошо объяснено в
http://www.rsdn.ru/article/atl/atlsingleton.xml
Автор(ы): Иван Андреев
Дата: 03.08.2003
Описание шаблона проектирования синглетон очень простое — синглетон представляет собой единственный экземпляр класса, с которым работают все клиенты. Применительно к COM шаблон проектирования синглетон гарантирует, что все вызовы CoCreateInstance будут возвращать указатель на интерфейс единственного экземпляра компонента. Удобство использования таких компонентов/классов заключается в том, что клиенты работают с одним и тем же экземпляром, а значит, получают доступ к разделяемому состоянию этого экземпляра. Несмотря на простое описание, не существует "идеальной" реализации этого шаблона ни в языке С++, ни для COM-объектов. Связано это с тем, что любая существующая реализация имеет некоторые ограничения и не может выступать в роли "универсальной" реализации на все случаи жизни.


3. Слишком грубая структуризация — несколько потоков не могут
одновременно использовать ресурсы одного аппартамента, даже если они не
связаны друг с другом.

4. Остается возможность дедлоков, несмотря на то, что код фактически
однопоточный.

Из плюсов: очень эффективно в некоторых случаях, так как не нужны
блокировки ну и писать однопоточные приложения значительно проще.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re: lock (this)
От: Дарней Россия  
Дата: 29.03.06 16:25
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>3) Для синхронизированного (эксклюзивного) доступа к данным используются эксклюзивные методы (т.е такие методы, блоки кода в которых залочены на this).


и всё-таки, я так и не понял толком — лочатся все методы объекта или только специальным образом помеченные?
и второй вопрос — зачем писать в явном виде вот это, вместо простого lock() {}?
System.Monitor.Enter(this);
  try
  {
  }
  finally
  {
    System.Monitor.PulseAll(this);
    System.Monitor.Exit(this);
  }
... << RSDN@Home 1.1.4 stable rev. 510>>
Всех излечит, исцелит
добрый Ctrl+Alt+Delete
Re[8]: Решение задачи об очереди
От: ie Россия http://ziez.blogspot.com/
Дата: 29.03.06 16:36
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

ie>>В том то и беда. Что вызов Dequeue может привести к бесконечному ожиданию, что на самом деле не допустимо.


СГ> В некоторых задачах очень даже допустимо, даже необходимо по логике работы. Например в моей программе есть такое место, когда распознаватель речи обратившись за очередной порцией данных отправляется в спячку если эта порция данных ещё не была к этому времени получена. Как только она получается, он автоматически будится просто от того, что в очередь произведена запись.


У меня тоже такие задачи регулярны, но отслеживать ситуацию, когда клиент просто забил и перестал слать какие-либо данные отслеживаться должна непременно. Но вот делать такую коллекцию, как предлагаете вы (спящую до появления данных), по меньшей мере не логично. Либо это должна быть коллекция "под конкретную задачу", но ни как не общего назначения.

ie>>Решение разработчиков стандартной (System.Collection.Queue) очереди мне кажется более логичным. Но как я помню из ваших предыдущих постов, бросать исключение плохо, поэтому даже не знаю получится ли у вас красивое разрешение этой ситуации.


СГ>Не блокирующий вызов не кидающий исключений в случае пустой очереди? Да элементарно:

СГ>
СГ>bool Dequeue (out object obj);
СГ>


Я бы в таком случае принял решение создания 2х методов.

Кидающий исключение:
СГ>
СГ>object Dequeue();
СГ>

И не кидающий его:
СГ>
СГ>bool TryDequeue(out object obj);
СГ>
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Re: lock (this)
От: Кодт Россия  
Дата: 29.03.06 19:01
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

Д>> Вот придёт кому-то в голову синхронизироваться на этом же объекте, но в другом месте и в других целях — и всё, приплыли.


СГ>Ну, дуракам закон не писан. А Вы ещё спрашивали в чём разница между обычными языками (типа C#) и языками поддерживающими парадигму активных объектов (Active Oberon, Zonnon,...). Разница в том, что залочится в C# можно на любом (чужом) объекте (быть может, на ровном месте неожиданно заработав deadlock), а в Active Oberon — только на самом себе (только на this).


Сергей, а как в Обероне решается вот такая задача:
TYPE ClassA = CLASS
  ptrB: POINTER TO ClassB;
END;
TYPE ClassB = CLASS
  ptrA: POINTER TO ClassA;
END;


FUNCTION Construct(): POINTER TO ClassA;
BEGIN
  ptrA := NEW ClassA;
  ptrA.ptrB := NEW ClassB;
  ptrA.ptrB.ptrA := ptrA;
  Construct := ptrA;
END;


PROCEDURE ClassA.foo;
BEGIN {exclusive}
  .....
  this.ptrB.foo();
  .....
END;
PROCEDURE ClassB.foo;
BEGIN {exclusive}
  .....
END;


PROCEDURE ClassB.bar;
BEGIN {exclusive}
  .....
  this.ptrA.bar();
  .....
END;
PROCEDURE ClassA.bar;
BEGIN {exclusive}
  .....
END;

(Если чего напутал в синтаксисе — извини).

И теперь предположим, что два потока одновременно влезли в эту пару — один через ClassA.foo(), другой через ClassB.bar(). Всё, дедлок. Хотя объекты блокировали только себя.

По-хорошему, тут нужно всё переписывать: разбивать ClassA.foo и ClassB.bar, чтобы передача управления за пределы критической секции разблокировала объект. Естественно, не нарушая его инвариант...
Перекуём баги на фичи!
Re: lock (this)
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 29.03.06 23:28
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Лочиться только на самом себе логически следует из объединения ООП и многопоточности:

СГ>1) Все данные инкапсулированы внутри объектов.
СГ>2) Доступ к любым данным осуществляется только с помощью методов объекта.
СГ>3) Для синхронизированного (эксклюзивного) доступа к данным используются эксклюзивные методы (т.е такие методы, блоки кода в которых залочены на this).

А как лочить несколько объектов за раз? То есть, например, вот такая ситуация:

Имеем два объекта (a1 и a2). И два объекта-пользователя (b и c). Объекту b нужно лочить a1 и a2, а объекту c — только a2. Например, b перекачивает какие-то данные из a1 в a2, а c — только обновляет a2.

Если мы можем опираться только на самоблокировку объекта, тогда получается, что нужно организовать какой-то промежуточный d, у которого будет два exclusive-метода: meth_for_a1_a2 и meth_for_a2. Хорошо, мы перенаправим вызовы b и c на некий d. А потом у нас появится третий пользователь, например e, которому нужно будет лочить только a1. Будем переделывать d?
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re: lock (this)
От: Кодт Россия  
Дата: 29.03.06 23:52
Оценка: +3
Здравствуйте, Сергей Губанов, Вы писали:

<>

Некоторый оффтопик.

В очередной раз дискуссия переходит в режим противостояния. И вот почему.
Ты предлагаешь серебряные пули. А серебряных пуль-то, на самом деле, не существует. Есть много видов обычных пуль, которыми нужно стрелять по соответствующим целям.

У самоблокировок есть своя ниша. У блокировок сторонних объектов — другая ниша.
Скажем, в СУБД предмет блокировок — не сами активности сервера, и не транзакции, а участки БД, т.е. пассивные объекты. Делать таблицы активностями просто смысла нет.

Кстати, джентельмены! Чтобы не доводить до флейма: когда приводите контрпримеры — старайтесь не просто "доказать от противного", но и
— очертить круг целей
— показать, какие пули предназначены для этих целей
Перекуём баги на фичи!
Re[2]: lock (this)
От: Severn Россия  
Дата: 30.03.06 03:30
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ> Будем переделывать d?

Зачем? d синхронизирует свои данные — по сути это некая дополнителная обработка между обращениями к a1 и a2.


    public class A1
    {
        public Smth Smth
        {
            get { return new Smth("Smth from A1"); }
        }

        public void DoA1(A2 a2)
        {
            Smth s = a2.Smth;
            lock (this)
            {
                // некие операции с иполоьзованием this и s
            }
        }
    }

    public class A2
    {
        public Smth Smth
        {
            get { return new Smth("Smth from A2"); }
        }

        public void DoA2(A1 a1)
        {
            Smth s = a1.Smth;
            lock (this)
            {
                // некие операции с иполоьзованием this и s
            }
        }
        public void MethForA2()
        {
            lock (this)
            {
                // некие операции с иполоьзованием this
            }
        }
    }

    public class D
    {
        public void MethForA1A2(A1 a1, A2 a2)
        {
            // синхронизирует все операцию целиком, а не данные a1 и a2
            // если additionalProcessing нет, то просто переносим все либо в A1 либо в A2
            lock (this)
            {
                a1.DoA1(a2);
                additionalProcessing(a1,a2);
                a2.DoA2(a1);
            }
        }

        private void additionalProcessing(A1 a1, A2 a2)
        { }
    }

Подправил форматирование — включил раскраску кода. — Кодт
Re[9]: Решение задачи об очереди
От: Severn Россия  
Дата: 30.03.06 03:39
Оценка:
Здравствуйте, ie, Вы писали:

ie>Кидающий исключение:

СГ>>
СГ>>object Dequeue();
СГ>>

ie>И не кидающий его:
СГ>>
СГ>>bool TryDequeue(out object obj);
СГ>>


Это имхо неверно — ислпьзовать два разных подхода. Никогда не знаешь, что от метода ожидать — придется и if'ы лепить и try.
Лучше уж сделать
object Dequeue();

НЕ кидающий исключение и при случае возвращающий null. Дополнительный if все равно придется ставить, также как и в bool TryDequeue(out object obj), а писанины меньше
Re[3]: Решение задачи об очереди
От: Severn Россия  
Дата: 30.03.06 03:44
Оценка:
Здравствуйте, Severn, Вы писали:

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


Кстати, неплохая статейка есть: здесь. Правда имхо не нужно все понимать так радикально, как там написано.
Re[9]: Решение задачи об очереди
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.03.06 07:40
Оценка:
Здравствуйте, ie, Вы писали:

ie> это должна быть коллекция "под конкретную задачу"


Естественно.
Re[2]: lock (this)
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.03.06 07:57
Оценка:
Здравствуйте, Дарней, Вы писали:

Д>и всё-таки, я так и не понял толком — лочатся все методы объекта или только специальным образом помеченные?


Лочатся только блоки кода помеченные директивой {EXCLUSIVE}. Внутри одной процедуры может быть несколько таких блоков.
PROCEDURE DoSmth;
BEGIN
  ...
  BEGIN {EXCLUSIVE} this.a := a1; this.b := b1 END; (* значения измененяются одной атомарной операцией *)
  ...
  BEGIN {EXCLUSIVE} this.a := a2; this.b := b2 END; (* значения измененяются одной атомарной операцией *)
  ...
END DoSmth;


Д>и второй вопрос — зачем писать в явном виде вот это, вместо простого lock() {}?


Я не настаиваю на этом. Можете писать как хотите:
System.Threading.Monitor.Enter(this);
try
{
  ...
  ...
  ...
  return x;
}
finally
{
  System.Threading.Monitor.PulseAll(this);
  System.Threading.Monitor.Exit(this);
}

или через lock:
lock (this)
{
  try
  {
    ...
    ...
    ...
    return x;
  } 
  finally
  {
    System.Threading.Monitor.PulseAll(this);
  }
}

эти тексты эквивалентны.

Естественно, если Вы не пользуетесь связкой Monitor.Wait(this) / Monitor.PulseAll(this), то lock — более "красиво выглядит".
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.