Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 10:11
Оценка:
У меня есть одна длительная операция, выполняющаяся с отдельном потоке и требующая ресурс А. Есть множество коротких операций выполняющихся в других потоках. Каждая из этих операций по отдельности также требует ресурс А. Если не принять специальных мер, то любая короткая операция будет ждать завершения длительной. Но в длительной операции у меня есть места, в которых я могу разрешить коротким операциям выполниться. Сделал я это следующим образом (код компилируется):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DiscontinuousLock
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread longWorkthread = new Thread(() =>
            {
                Monitor.Enter(typeof(Program));
                try
                {
                    for (int i = 0; i < 25; i++)
                    {
                        Thread.Sleep(100);
                        Console.WriteLine("Long work thread have done a bit #{0} of his long work ", i);
                        Monitor.Exit(typeof(Program));
                        Monitor.Enter(typeof(Program));
                    }
                }
                finally
                {
                    Monitor.Exit(typeof(Program));
                }
            });

            Thread manyShortWorksThread = new Thread(() =>
            {
                for (int i = 0; i < 250; i++)
                {
                    Monitor.Enter(typeof(Program));
                    try
                    {
                        Thread.Sleep(5);
                        Console.WriteLine("Many short works thread have done one short work #{0}", i);
                    }
                    finally
                    {
                        Monitor.Exit(typeof(Program));
                    }
                }
            });

            longWorkthread.Start();
            manyShortWorksThread.Start();

            longWorkthread.Join();
            manyShortWorksThread.Join();

            Console.ReadLine();
        }
    }
}


1. Можно ли так делать? Какие могут быть проблемы?
2. Есть ли более цивилизованные варианты?
3. Можно использовать данный способ для борьбы с dead lock?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
lock
Re: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 09.03.13 10:34
Оценка: 1 (1)
Для этих дел у монитора есть Monitor.Pulse и Monitor.PulseAll
Re: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 09.03.13 10:36
Оценка:
Да и закат солнца вручную, в виде росписи лока в try/finally не добавляет наглядности тем более что расписано старым методом, неустойчивым к Thread.Abort
Re[2]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 10:42
Оценка:
_>Для этих дел у монитора есть Monitor.Pulse и Monitor.PulseAll

Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[2]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 10:44
Оценка:
_>Да и закат солнца вручную, в виде росписи лока в try/finally не добавляет наглядности тем более что расписано старым методом, неустойчивым к Thread.Abort

Как выглядит try/finally эквивалент lock устойчивый ThreadAbort?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re: Прерывистая блокировки
От: cvetkov  
Дата: 09.03.13 10:45
Оценка: +1
Здравствуйте, igor-booch, Вы писали:


IB>1. Можно ли так делать?

да.
IB>Какие могут быть проблемы?
одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока.
IB>2. Есть ли более цивилизованные варианты?
паттерн unit of work
IB>3. Можно использовать данный способ для борьбы с dead lock?
нет.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re: Прерывистая блокировки
От: andrey82  
Дата: 09.03.13 10:53
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>У меня есть одна длительная операция, выполняющаяся с отдельном потоке и требующая ресурс А. Есть множество коротких операций выполняющихся в других потоках. Каждая из этих операций по отдельности также требует ресурс А.


А какой доступ требуется к этому ресурсу? Если по отдельности только чтение или запись, то есть хороший вариант синхрогизации с использованием ReaderWriterLock или ReaderWriterLockSlim.

IB>1. Можно ли так делать? Какие могут быть проблемы?

IB>2. Есть ли более цивилизованные варианты?

IB>
IB>Monitor.Enter(typeof(Program));
IB>


Лучше для синхронизации доступа использовать что-то типа
readonly object locker = new object();
Re[2]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 11:00
Оценка:
A>А какой доступ требуется к этому ресурсу? Если по отдельности только чтение или запись, то есть хороший вариант синхрогизации с использованием ReaderWriterLock или ReaderWriterLockSlim.
доступ требуется эксклюзивный

IB>>
IB>>Monitor.Enter(typeof(Program));
IB>>

A>Лучше для синхронизации доступа использовать что-то типа
A>
A>readonly object locker = new object();
A>


Чем лучше?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[2]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 11:03
Оценка:
IB>>Какие могут быть проблемы?
C>одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока.
Это вопрос правильного применения метода, а не самого метода

IB>>2. Есть ли более цивилизованные варианты?

C>паттерн unit of work
А он разве не БД относится?

IB>>3. Можно использовать данный способ для борьбы с dead lock?

C>нет.
Почему? Потому-что не получится или потому-что получится, но возникнут проблемы?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[3]: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 09.03.13 11:04
Оценка: 2 (1)
IB>Как выглядит try/finally эквивалент lock устойчивый ThreadAbort?

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }


До C#4 роспись была неправильной, подробности в блоге Эрика Липперта
Re[3]: Прерывистая блокировки
От: andrey82  
Дата: 09.03.13 11:17
Оценка: 2 (1)
Здравствуйте, igor-booch, Вы писали:

IB>Чем лучше?


Локика блокировки получается не отделена от остального... в общем, Joseph Albahari &mdash; Threading in C#, раздел Choosing the Synchronization Object, там хорошо объясняется.
Re[3]: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 09.03.13 11:20
Оценка: 2 (1)
IB>Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.

В смысле? Это не работает?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DiscontinuousLock
{
    class Program
    {
        static readonly object SyncRoot = new object();

        static void Main(string[] args)
        {
            var longWorkthread = new Thread(() =>
            {
                lock(SyncRoot)
                {
                    for(var i = 0; i < 25; i++)
                    {
                        Thread.Sleep(100);
                        Console.WriteLine("Long work thread have done a bit #{0} of his long work ", i);
                        Monitor.Wait(SyncRoot);//здесь происходит временный выход из лока и разрешение работать потокам short work
                    }
                }
            });

            var manyShortWorksThread = new Thread(() =>
            {
                for(var i = 0; i < 250; i++)
                {
                    lock(SyncRoot)
                    {
                        Thread.Sleep(5);
                        Console.WriteLine("Many short works thread have done one short work #{0}", i);
                        Monitor.Pulse(SyncRoot);//здесь мы сообщаем LongWork потоку что он может продолжить работу, в зависимости от реального приложения может потребоваться PulseAll
                    }                    
                }
            });

            longWorkthread.Start();
            manyShortWorksThread.Start();

            longWorkthread.Join();
            manyShortWorksThread.Join();

            Console.ReadLine();
        }
    }
}
Re[3]: Прерывистая блокировки
От: cvetkov  
Дата: 09.03.13 11:31
Оценка:
Здравствуйте, igor-booch, Вы писали:

IB>>>Какие могут быть проблемы?

C>>одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока.
IB>Это вопрос правильного применения метода, а не самого метода
ну вы спрашивали про потенциальные проблемы.

IB>>>2. Есть ли более цивилизованные варианты?

C>>паттерн unit of work
IB>А он разве не БД относится?
нет. его можно применять к бд, но нет причин ограничиватся только бд
IB>>>3. Можно использовать данный способ для борьбы с dead lock?
C>>нет.
IB>Почему? Потому-что не получится или потому-что получится, но возникнут проблемы?
не получится. само по себе применение такого подхода не спасает от дедлоков.
... << RSDN@Home 1.2.0 alpha 5 rev. 1539>>
Re[4]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 09.03.13 16:08
Оценка:
Здравствуйте, hi_octane, Вы писали:

IB>>Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.


_>В смысле? Это не работает?

Я использовал Pulse не так как Вы. А чем ваш метод может быть лучше моего, кроме ? Мой метод мне пока нравится больше, так как
1) у меня после исполнения a bit of his long work могут выполниться несколько short works успевших встать в очередь.
2) у меня затрагивается код только длительных операций.

Кстати я бы изменил Ваш код так:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DiscontinuousLock
{
    class Program
    {
        static readonly object SyncRoot = new object();

        static void Main(string[] args)
        {
            var longWorkthread = new Thread(() =>
            {
                lock(SyncRoot)
                {
                    for(var i = 0; i < 25; i++)
                    {
                        Thread.Sleep(100);
                        Console.WriteLine("Long work thread have done a bit #{0} of his long work ", i);
                        Monitor.Wait(SyncRoot);//здесь происходит временный выход из лока и разрешение работать потокам short work
                    }
                }
            });

            var manyShortWorksThread = new Thread(() =>
            {
                for(var i = 0; i < 250; i++)
                {
                    lock(SyncRoot)
                    {
                        try
                        {
                            Thread.Sleep(5);
                            Console.WriteLine("Many short works thread have done one short work #{0}", i);
                        }
                        finnaly
                        {
                            Monitor.Pulse(SyncRoot);//здесь мы сообщаем LongWork потоку что он может продолжить работу, в зависимости от реального приложения может потребоваться PulseAll
                        }                                                                    
                    }                    
                }
            });

            longWorkthread.Start();
            manyShortWorksThread.Start();

            longWorkthread.Join();
            manyShortWorksThread.Join();

            Console.ReadLine();
        }
    }
}
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[5]: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 10.03.13 11:12
Оценка: 1 (1)
IB>>>Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.
_>>В смысле? Это не работает?
IB>Я использовал Pulse не так как Вы. А чем ваш метод может быть лучше моего, кроме ? Мой метод мне пока нравится больше, так как


IB>1) у меня после исполнения a bit of his long work могут выполниться несколько short works успевших встать в очередь.

Это технические вопросы. Можно вокруг wait поставить цикл и таймер, чтоб самому решать сколько времени максимально выделить на short-work'и.

IB>2) у меня затрагивается код только длительных операций.

Да это плюс.

IB>Кстати я бы изменил Ваш код так:

Тут уже от конкретики зависит. С try/finally лучше если исключение в short-work можно игнорировать и оно гарантированно не нарушает защищаемого lock'ом состояния. Если нарушает — то вплоть до longWorkthread.Abort() может быть нужно.



Решение на Monitor.Enter/Exit полагается на то что у монитора есть некая FIFO "очередь Enter'ов" и блокировка происходит именно в том порядке в котором потоки вызвали lock. Почти всегда это так, но, особенно на многопроцессорных системах, возможны чудеса. Элементарно может получиться ситуация когда short-work'и раз за разом получают входят в lock и выполняются снова и снова, а long-work ждёт, ждёт, и никак не дождётся возможности закончить работу. Джон Скит подтверждает это и даёт ссылки на умные книжки.
Re[6]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 10.03.13 13:42
Оценка:
_>Решение на Monitor.Enter/Exit полагается на то что у монитора есть некая FIFO "очередь Enter'ов" и блокировка происходит именно в том порядке в котором потоки вызвали lock. Почти всегда это так, но, особенно на многопроцессорных системах, возможны чудеса.

При нарушении порядка входов в блокировки возможны deadlocks.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[7]: Прерывистая блокировки
От: hi_octane Беларусь  
Дата: 11.03.13 15:40
Оценка:
IB>При нарушении порядка входов в блокировки возможны deadlocks.
В смысле? Какой может быть в принципе порядок входа если объект один?
Re: Прерывистая блокировки
От: igor-booch Россия  
Дата: 11.03.13 17:50
Оценка: +1 -1
У меня один блокировки, но если их несколько есть особенность

lock(A)
    lock(B)
    {
        Monitor.Exit(A);
        Monitor.Enter(A);
    }


такой код вызовет dead lock
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[8]: Прерывистая блокировки
От: igor-booch Россия  
Дата: 11.03.13 17:50
Оценка:
_>В смысле? Какой может быть в принципе порядок входа если объект один?

Это же простой пример, объектов блокировки может быть несколько, в моем приложении это так.
Кстати http://rsdn.ru/forum/dotnet/5096671.1
Автор: igor-booch
Дата: 11.03.13
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания
http://rsdn.ru/Info/rules.xml
Re[3]: Прерывистая блокировки
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 11.03.13 19:00
Оценка:
Здравствуйте, igor-booch, Вы писали:

_>>Для этих дел у монитора есть Monitor.Pulse и Monitor.PulseAll


IB>Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.


Однако, у тебя классическая задача, которая именно через мониторы и решается на раз.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.