У меня есть одна длительная операция, выполняющаяся с отдельном потоке и требующая ресурс А. Есть множество коротких операций выполняющихся в других потоках. Каждая из этих операций по отдельности также требует ресурс А. Если не принять специальных мер, то любая короткая операция будет ждать завершения длительной. Но в длительной операции у меня есть места, в которых я могу разрешить коротким операциям выполниться. Сделал я это следующим образом (код компилируется):
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
Да и закат солнца вручную, в виде росписи лока в try/finally не добавляет наглядности тем более что расписано старым методом, неустойчивым к Thread.Abort
_>Для этих дел у монитора есть Monitor.Pulse и Monitor.PulseAll
Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
_>Да и закат солнца вручную, в виде росписи лока в try/finally не добавляет наглядности тем более что расписано старым методом, неустойчивым к Thread.Abort
Как выглядит try/finally эквивалент lock устойчивый ThreadAbort?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
IB>1. Можно ли так делать?
да. IB>Какие могут быть проблемы?
одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока. IB>2. Есть ли более цивилизованные варианты?
паттерн unit of work IB>3. Можно использовать данный способ для борьбы с dead lock?
нет.
Здравствуйте, igor-booch, Вы писали:
IB>У меня есть одна длительная операция, выполняющаяся с отдельном потоке и требующая ресурс А. Есть множество коротких операций выполняющихся в других потоках. Каждая из этих операций по отдельности также требует ресурс А.
А какой доступ требуется к этому ресурсу? Если по отдельности только чтение или запись, то есть хороший вариант синхрогизации с использованием ReaderWriterLock или ReaderWriterLockSlim.
IB>1. Можно ли так делать? Какие могут быть проблемы? IB>2. Есть ли более цивилизованные варианты?
IB>
IB>Monitor.Enter(typeof(Program));
IB>
Лучше для синхронизации доступа использовать что-то типа
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
IB>>Какие могут быть проблемы? C>одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока.
Это вопрос правильного применения метода, а не самого метода
IB>>2. Есть ли более цивилизованные варианты? C>паттерн unit of work
А он разве не БД относится?
IB>>3. Можно использовать данный способ для борьбы с dead lock? C>нет.
Почему? Потому-что не получится или потому-что получится, но возникнут проблемы?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
IB>Чем лучше?
Локика блокировки получается не отделена от остального... в общем, Joseph Albahari — Threading in C#, раздел Choosing the Synchronization Object, там хорошо объясняется.
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();
}
}
}
Здравствуйте, igor-booch, Вы писали:
IB>>>Какие могут быть проблемы? C>>одна из проблем неконтролируемые потоки информации. между сеансами обработки может передаватя информация которая становится невалидной после отпускания лока. IB>Это вопрос правильного применения метода, а не самого метода
ну вы спрашивали про потенциальные проблемы.
IB>>>2. Есть ли более цивилизованные варианты? C>>паттерн unit of work IB>А он разве не БД относится?
нет. его можно применять к бд, но нет причин ограничиватся только бд IB>>>3. Можно использовать данный способ для борьбы с dead lock? C>>нет. IB>Почему? Потому-что не получится или потому-что получится, но возникнут проблемы?
не получится. само по себе применение такого подхода не спасает от дедлоков.
Здравствуйте, 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
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 ждёт, ждёт, и никак не дождётся возможности закончить работу. Джон Скит подтверждает это и даёт ссылки на умные книжки.
_>Решение на Monitor.Enter/Exit полагается на то что у монитора есть некая FIFO "очередь Enter'ов" и блокировка происходит именно в том порядке в котором потоки вызвали lock. Почти всегда это так, но, особенно на многопроцессорных системах, возможны чудеса.
При нарушении порядка входов в блокировки возможны deadlocks.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
_>>Для этих дел у монитора есть Monitor.Pulse и Monitor.PulseAll
IB>Monitor.Pulse это несколько другое. Попробуйте с ним запустить приложение, работать не будет.
Однако, у тебя классическая задача, которая именно через мониторы и решается на раз.