lock с приоритетом
От: sergunok  
Дата: 10.02.11 14:49
Оценка:
Такое есть в современном .NET?

Или нужно реализовывать самому?
Re: lock с приоритетом
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.02.11 14:50
Оценка: 1 (1) +1
Здравствуйте, sergunok, Вы писали:

Поведение сабжа не ясно. Можете привести сценарий использования, а лучше задачу где вам такое надо?
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: lock с приоритетом
От: Aen Sidhe Россия Просто блог
Дата: 10.02.11 14:53
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Такое есть в современном .NET?


S>Или нужно реализовывать самому?


lock — это обычный монитор. Какой ещё "монитор с приоритетом"?
С уважением, Анатолий Попов.
ICQ: 995-908
Re[2]: lock с приоритетом
От: sergunok  
Дата: 10.02.11 14:57
Оценка:
Здравствуйте, adontz, Вы писали:

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


A>Поведение сабжа не ясно. Можете привести сценарий использования, а лучше задачу где вам такое надо?


что-то типа:


// нити типа А
megalock.Lock(5)
...
megalock.Unlock()

// нити типа Б
megalock.Lock(3)
...
megalock.Unlock()


Если к примеру сейчас критическую секцию захватила нить типа А и ломятся еще и другие нити разных типов, в первую очередь будут обслуживаться нити типа (т.к. их приоритет выше)
Re[2]: lock с приоритетом
От: sergunok  
Дата: 10.02.11 14:59
Оценка:
Здравствуйте, Aen Sidhe, Вы писали:

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


S>>Такое есть в современном .NET?


S>>Или нужно реализовывать самому?


AS>lock — это обычный монитор. Какой ещё "монитор с приоритетом"?


Пусть монитор, меня интересует критическая секция с возможностью приоритезации.. Чем реализуется — семафором или монитором — не важно.
Re[3]: lock с приоритетом
От: adontz Грузия http://adontz.wordpress.com/
Дата: 10.02.11 14:59
Оценка: +1
Здравствуйте, sergunok, Вы писали:

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


А почему нельзя задать нитям приоритет?

http://msdn.microsoft.com/en-us/library/system.threading.thread.priority.aspx
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[4]: lock с приоритетом
От: sergunok  
Дата: 10.02.11 15:03
Оценка:
Здравствуйте, adontz, Вы писали:

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


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


A>А почему нельзя задать нитям приоритет?


A>http://msdn.microsoft.com/en-us/library/system.threading.thread.priority.aspx


Хочется более нативный способ
Re[3]: lock с приоритетом
От: fddima  
Дата: 10.02.11 21:41
Оценка:
Здравствуйте, sergunok, Вы писали:

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

Если секция уже захвачена — то как другие её захватят? Куда денется поток А?
Re[4]: lock с приоритетом
От: Lloyd Россия  
Дата: 10.02.11 21:45
Оценка: 3 (2)
Здравствуйте, fddima, Вы писали:

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

F> Если секция уже захвачена — то как другие её захватят? Куда денется поток А?

Имеется в виду, что после освобождения секции потоком А управление передастся тому потоку, кто передал большее значение в метод Lock.
Re[5]: lock с приоритетом
От: Jolly Roger  
Дата: 11.02.11 04:28
Оценка: +1
Здравствуйте, sergunok, Вы писали:

A>>А почему нельзя задать нитям приоритет?


S>Хочется более нативный способ


Вы о чём? Куда ещё нативней? Lock работает в Ring3, пока не нарвётся на уже залоченнный кем-то объект, после этого управление передаётся ядру. Когда объект освободится, уже ядро принимает решение какому из ожидающих потоков его отдать. Алгоритм этого выбора не документирован и по факту разный в разных ОС. Единственный способ повлиять на этот выбор из ring3 — установка приоритета потоков.

Слепить такой механизм в ring3 конечно можно, но боюсь, он получится громоздкий и неповоротливый, сомневаюсь в целесобразности его использования.
"Нормальные герои всегда идут в обход!"
Re[3]: lock с приоритетом
От: XRonos Россия  
Дата: 11.02.11 05:32
Оценка:
Здравствуйте, sergunok, Вы писали:

S>что-то типа:

S>// нити типа А
S>megalock.Lock(5); ... КодА ... megalock.Unlock();
S>// нити типа Б
S>megalock.Lock(3); ... КодБ ... megalock.Unlock();
S>Если к примеру сейчас критическую секцию захватила нить типа А и ломятся еще и другие нити разных типов, в первую очередь будут обслуживаться нити типа (т.к. их приоритет выше)

Давай порассуждаем:
Если захвачена секция A, значит код этой секции выполняется (представим, что это происходит достаточно плавно — очень мелкими тактами).
Через N времени поток Б дошёл до точки блокировки, которая имеет, допустим, больший приоритет.
Вопрос: в какой части кода (КодА), между Lock Unlock, поток А должен встать в ожидание?
На этот вопрос можно ответить, только если КодА будет мелко прорежен проверками чтобы встать на ожидание.
Получится чрезвычайно затратный оверхедный код.

А вот ситуация, когда несколько потоков подходят одновременно к точке блокировки — практически равна нулю.


Поэтому, чтобы избежать гонки за ресурсы и правильно распределить вычислительные нагрузки, можно воспользоваться
другой техникой:
Разбивать код на более мелкие секции, но при этом пользоваться классом SpinLock вместо lock.
Опять таки это зависит от конкретной задачи и не факт, что это будет работать оптимальнее.
Бди!
Re[6]: lock с приоритетом
От: sergunok  
Дата: 11.02.11 08:23
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


A>>>А почему нельзя задать нитям приоритет?


S>>Хочется более нативный способ


JR>Вы о чём? Куда ещё нативней? Lock работает в Ring3, пока не нарвётся на уже залоченнный кем-то объект, после этого управление передаётся ядру. Когда объект освободится, уже ядро принимает решение какому из ожидающих потоков его отдать. Алгоритм этого выбора не документирован и по факту разный в разных ОС. Единственный способ повлиять на этот выбор из ring3 — установка приоритета потоков.


JR>Слепить такой механизм в ring3 конечно можно, но боюсь, он получится громоздкий и неповоротливый, сомневаюсь в целесобразности его использования.


Приоритет повлияет на планирование выполнения нитей.
Re[7]: lock с приоритетом
От: Jolly Roger  
Дата: 11.02.11 08:51
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Приоритет повлияет на планирование выполнения нитей.


Разумеется, но разве Вам не это по сути надо? Может Вы всё-таки расскажете, зачем это Вам понадобилось?
"Нормальные герои всегда идут в обход!"
Re[8]: lock с приоритетом
От: sergunok  
Дата: 11.02.11 09:05
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

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


S>>Приоритет повлияет на планирование выполнения нитей.


JR>Разумеется, но разве Вам не это по сути надо? Может Вы всё-таки расскажете, зачем это Вам понадобилось?


Влиять на распределение процессорного времени между нитями в качестве побочного эффекта мне нужно.

Все, что нужно, — в начале критической секции выстраивать очередь на попадание в оную согласно явно заданным приоритетам.
Re[9]: lock с приоритетом
От: Jolly Roger  
Дата: 11.02.11 09:15
Оценка: 3 (1)
Здравствуйте, sergunok, Вы писали:

S>Влиять на распределение процессорного времени между нитями в качестве побочного эффекта мне нужно.


S>Все, что нужно, — в начале критической секции выстраивать очередь на попадание в оную согласно явно заданным приоритетам.


Что Вам нужно-не нкжно, я понял Я спрашивал зачем. Впрочем, как знаете.
Вот, набросал (даже запускать не пробовал), чтобы продемонстрировать возможный подход, посмотрите

  Скрытый текст
    #pragma warning disable 420

    class PriorityLock: IDisposable
    {
        public static readonly int MaxPriority = 15;

        class LockFreeStack<T> where T : class
        {
            class StackItem<T1> where T1 : class
            {
                public T1 Value;
                public volatile StackItem<T1> Next = null;
                public StackItem(T1 data) { Value = data; }
            }

            private volatile StackItem<T> Head = null;

            public T Pop()
            {
                StackItem<T> tmpHead = Head;
                StackItem<T> tmpHead2;
                for (; ; )
                {
                    if (tmpHead == null) return null;
                    tmpHead2 = Interlocked.CompareExchange<StackItem<T>>(
                        ref Head, tmpHead.Next, tmpHead);
                    if (tmpHead2 == tmpHead)
                        return tmpHead.Value;
                    tmpHead = tmpHead2;
                }
            }

            public void Push(T data)
            {
                StackItem<T> item = new StackItem<T>(data);
                StackItem<T> tmpHead = Head;
                StackItem<T> tmpHead2;
                for (; ; )
                {
                    item.Next = tmpHead;
                    tmpHead2 = Interlocked.CompareExchange<StackItem<T>>(
                        ref Head, item, tmpHead);
                    if (tmpHead2 == tmpHead)
                        return;
                    tmpHead = tmpHead2;
                }
            }
        }
        struct Queueitem
        {
            public int ThreadID;
            public EventWaitHandle handle;
        }

        volatile int disposed = 0;
        volatile int lockOwner;
        int maxPriority;
        object innerLock = new object();
        Stack<Queueitem>[] queues;
        LockFreeStack<EventWaitHandle> freeEvents;

        public PriorityLock(int MaxPriorityLevel)
        {
            if (MaxPriorityLevel < 0 || MaxPriorityLevel > MaxPriority)
                throw new ArgumentException("", "MaxPriorityLevel");

            maxPriority = MaxPriorityLevel;
            freeEvents = new LockFreeStack<EventWaitHandle>();
            queues = new Stack<Queueitem>[maxPriority + 1];
            for (int i = 0; i <= maxPriority; i++)
            {
                queues[i] = new Stack<Queueitem>(16);
            }
        }

        public void Enter(int Level)
        {
            if (disposed != 0) throw new ObjectDisposedException("PriorityLock");
            try { }
            finally
            {
                if (Level < 0 || Level > maxPriority)
                    throw new ArgumentException("", "Level");
                var currentId = Thread.CurrentThread.ManagedThreadId;
                var currentOwner = 
                    Interlocked.CompareExchange(ref lockOwner, currentId, 0);
                if (currentOwner != 0)
                {
                    if (currentOwner == currentId)
                        throw new InvalidOperationException("Method isn't re-enterant");

                    EventWaitHandle ev = freeEvents.Pop();
                    if (ev == null) 
                        ev = new EventWaitHandle(false, EventResetMode.AutoReset);

                    lock (innerLock)
                    {
                        var item = new Queueitem();
                        item.ThreadID = currentId;
                        item.handle = ev;
                        queues[Level].Push(item);
                    }

                    ev.WaitOne();
                    freeEvents.Push(ev);
                }
            }
        }

        public void Leave()
        {
            if (disposed != 0) throw new ObjectDisposedException("PriorityLock");
            var ownerId = lockOwner;
            var currentId = Thread.CurrentThread.ManagedThreadId;
            if (ownerId != currentId)
                throw new InvalidOperationException(
                    "Current thread doesn't own the given object");

            try { }
            finally
            {
                int newOwner = 0;
                lock (innerLock)
                {
                    for (int i = maxPriority; i >= 0; i--)
                    {
                        if (queues[i].Count > 0)
                        {
                            var item = queues[i].Pop();
                            newOwner = item.ThreadID;
                            item.handle.Set();
                            break;
                        }
                    }
                    lockOwner = newOwner;
                }
            }
        }

        public void Dispose()
        {
            var d = Interlocked.Exchange(ref disposed, 1);
            if (d != 0) throw new ObjectDisposedException("PriorityLock");
            lock (innerLock)
            {
                for (int i = 0; i <= maxPriority; i++)
                {
                    foreach(var item in queues[i])
                    {
                        item.handle.Close();
                    }
                    queues[i].Clear();
                }

                do
                {
                    var ev = freeEvents.Pop();
                    if (ev != null) ev.Close();
                    else break;
                } while (true);
            }
        }
    }

PS Нереентерабельный, лень было делать
"Нормальные герои всегда идут в обход!"
Re[10]: lock с приоритетом
От: sergunok  
Дата: 13.02.11 10:54
Оценка:
Спасибо!

PS. В плане "зачем" — иначе и не скажешь, как приоритезация входа в крит.секцию.

Здравствуйте, Jolly Roger, Вы писали:

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


S>>Влиять на распределение процессорного времени между нитями в качестве побочного эффекта мне нужно.


S>>Все, что нужно, — в начале критической секции выстраивать очередь на попадание в оную согласно явно заданным приоритетам.


JR>Что Вам нужно-не нкжно, я понял Я спрашивал зачем. Впрочем, как знаете.

JR>Вот, набросал (даже запускать не пробовал), чтобы продемонстрировать возможный подход, посмотрите

JR>
  Скрытый текст
JR>    #pragma warning disable 420

JR>    class PriorityLock: IDisposable
JR>    {
JR>        public static readonly int MaxPriority = 15;

JR>        class LockFreeStack<T> where T : class
JR>        {
JR>            class StackItem<T1> where T1 : class
JR>            {
JR>                public T1 Value;
JR>                public volatile StackItem<T1> Next = null;
JR>                public StackItem(T1 data) { Value = data; }
JR>            }

JR>            private volatile StackItem<T> Head = null;

JR>            public T Pop()
JR>            {
JR>                StackItem<T> tmpHead = Head;
JR>                StackItem<T> tmpHead2;
JR>                for (; ; )
JR>                {
JR>                    if (tmpHead == null) return null;
JR>                    tmpHead2 = Interlocked.CompareExchange<StackItem<T>>(
JR>                        ref Head, tmpHead.Next, tmpHead);
JR>                    if (tmpHead2 == tmpHead)
JR>                        return tmpHead.Value;
JR>                    tmpHead = tmpHead2;
JR>                }
JR>            }

JR>            public void Push(T data)
JR>            {
JR>                StackItem<T> item = new StackItem<T>(data);
JR>                StackItem<T> tmpHead = Head;
JR>                StackItem<T> tmpHead2;
JR>                for (; ; )
JR>                {
JR>                    item.Next = tmpHead;
JR>                    tmpHead2 = Interlocked.CompareExchange<StackItem<T>>(
JR>                        ref Head, item, tmpHead);
JR>                    if (tmpHead2 == tmpHead)
JR>                        return;
JR>                    tmpHead = tmpHead2;
JR>                }
JR>            }
JR>        }
JR>        struct Queueitem
JR>        {
JR>            public int ThreadID;
JR>            public EventWaitHandle handle;
JR>        }

JR>        volatile int disposed = 0;
JR>        volatile int lockOwner;
JR>        int maxPriority;
JR>        object innerLock = new object();
JR>        Stack<Queueitem>[] queues;
JR>        LockFreeStack<EventWaitHandle> freeEvents;

JR>        public PriorityLock(int MaxPriorityLevel)
JR>        {
JR>            if (MaxPriorityLevel < 0 || MaxPriorityLevel > MaxPriority)
JR>                throw new ArgumentException("", "MaxPriorityLevel");

JR>            maxPriority = MaxPriorityLevel;
JR>            freeEvents = new LockFreeStack<EventWaitHandle>();
JR>            queues = new Stack<Queueitem>[maxPriority + 1];
JR>            for (int i = 0; i <= maxPriority; i++)
JR>            {
JR>                queues[i] = new Stack<Queueitem>(16);
JR>            }
JR>        }

JR>        public void Enter(int Level)
JR>        {
JR>            if (disposed != 0) throw new ObjectDisposedException("PriorityLock");
JR>            try { }
JR>            finally
JR>            {
JR>                if (Level < 0 || Level > maxPriority)
JR>                    throw new ArgumentException("", "Level");
JR>                var currentId = Thread.CurrentThread.ManagedThreadId;
JR>                var currentOwner = 
JR>                    Interlocked.CompareExchange(ref lockOwner, currentId, 0);
JR>                if (currentOwner != 0)
JR>                {
JR>                    if (currentOwner == currentId)
JR>                        throw new InvalidOperationException("Method isn't re-enterant");

JR>                    EventWaitHandle ev = freeEvents.Pop();
JR>                    if (ev == null) 
JR>                        ev = new EventWaitHandle(false, EventResetMode.AutoReset);

JR>                    lock (innerLock)
JR>                    {
JR>                        var item = new Queueitem();
JR>                        item.ThreadID = currentId;
JR>                        item.handle = ev;
JR>                        queues[Level].Push(item);
JR>                    }

JR>                    ev.WaitOne();
JR>                    freeEvents.Push(ev);
JR>                }
JR>            }
JR>        }

JR>        public void Leave()
JR>        {
JR>            if (disposed != 0) throw new ObjectDisposedException("PriorityLock");
JR>            var ownerId = lockOwner;
JR>            var currentId = Thread.CurrentThread.ManagedThreadId;
JR>            if (ownerId != currentId)
JR>                throw new InvalidOperationException(
JR>                    "Current thread doesn't own the given object");

JR>            try { }
JR>            finally
JR>            {
JR>                int newOwner = 0;
JR>                lock (innerLock)
JR>                {
JR>                    for (int i = maxPriority; i >= 0; i--)
JR>                    {
JR>                        if (queues[i].Count > 0)
JR>                        {
JR>                            var item = queues[i].Pop();
JR>                            newOwner = item.ThreadID;
JR>                            item.handle.Set();
JR>                            break;
JR>                        }
JR>                    }
JR>                    lockOwner = newOwner;
JR>                }
JR>            }
JR>        }

JR>        public void Dispose()
JR>        {
JR>            var d = Interlocked.Exchange(ref disposed, 1);
JR>            if (d != 0) throw new ObjectDisposedException("PriorityLock");
JR>            lock (innerLock)
JR>            {
JR>                for (int i = 0; i <= maxPriority; i++)
JR>                {
JR>                    foreach(var item in queues[i])
JR>                    {
JR>                        item.handle.Close();
JR>                    }
JR>                    queues[i].Clear();
JR>                }

JR>                do
JR>                {
JR>                    var ev = freeEvents.Pop();
JR>                    if (ev != null) ev.Close();
JR>                    else break;
JR>                } while (true);
JR>            }
JR>        }
JR>    }

JR>PS Нереентерабельный, лень было делать
Re[4]: lock с приоритетом
От: sergunok  
Дата: 13.02.11 10:56
Оценка:
Не очень понял как использование спинлоков поможет приоритизировать попадание в крит.секцию.


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

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


S>>что-то типа:

S>>// нити типа А
S>>megalock.Lock(5); ... КодА ... megalock.Unlock();
S>>// нити типа Б
S>>megalock.Lock(3); ... КодБ ... megalock.Unlock();
S>>Если к примеру сейчас критическую секцию захватила нить типа А и ломятся еще и другие нити разных типов, в первую очередь будут обслуживаться нити типа (т.к. их приоритет выше)

XR>Давай порассуждаем:

XR>Если захвачена секция A, значит код этой секции выполняется (представим, что это происходит достаточно плавно — очень мелкими тактами).
XR>Через N времени поток Б дошёл до точки блокировки, которая имеет, допустим, больший приоритет.
XR>Вопрос: в какой части кода (КодА), между Lock Unlock, поток А должен встать в ожидание?
XR>На этот вопрос можно ответить, только если КодА будет мелко прорежен проверками чтобы встать на ожидание.
XR>Получится чрезвычайно затратный оверхедный код.

XR>А вот ситуация, когда несколько потоков подходят одновременно к точке блокировки — практически равна нулю.



XR>Поэтому, чтобы избежать гонки за ресурсы и правильно распределить вычислительные нагрузки, можно воспользоваться

XR>другой техникой:
XR>Разбивать код на более мелкие секции, но при этом пользоваться классом SpinLock вместо lock.
XR>Опять таки это зависит от конкретной задачи и не факт, что это будет работать оптимальнее.
Re[5]: lock с приоритетом
От: adontz Грузия http://adontz.wordpress.com/
Дата: 13.02.11 10:59
Оценка:
Здравствуйте, sergunok, Вы писали:

топпостинг! оверквотинг!
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[6]: lock с приоритетом
От: sergunok  
Дата: 13.02.11 11:10
Оценка:
A>топпостинг! оверквотинг!

Спасиб! Учтем на будущее..

Век живи — век учись. Даже и слов таких раньше не знал
Re[11]: lock с приоритетом
От: Jolly Roger  
Дата: 13.02.11 13:48
Оценка:
Здравствуйте, sergunok, Вы писали:

S>Спасибо!


Только не забывайте, что это всего лишь набросок, который мной не то что не тестировался — даже не запускался, да и не анализировался. Будьте осторожны.
"Нормальные герои всегда идут в обход!"
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.