Есть объект ManualResetEvent.
В одном ("первом") потоке при одних условиях ему может делаться Set, а в другом ("втором") потоке — Reset.
Вопрос в следующем. Надо ли синхронизировать доступ к ManualResetEvent при доступе к нему из разных потоков?
Ведь возможна следующая ситуация. Пусть N каких-то "других" потоков ждут на этом ивенте (ManualresetEvent.WaitOne).
Пусть "первый" поток делает ManualResetEvent.Set чтобы "отпустить" все N ждущих потоков.
А в следующий квант процессорного времени "второй" поток делает этуму же ивенту ManualResetEvent.Reset.
Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого", т.е. не все N ждущих потоков будут освобожденны (или вообще ни один из них) когда "первый" поток вызвал Set?
Здравствуйте, <Аноним>, Вы писали:
А>Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого", т.е. не все N ждущих потоков будут освобожденны (или вообще ни один из них) когда "первый" поток вызвал Set?
Может.
... << RSDN@Home 1.2.0 alpha rev. 789>>
Re[2]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
12.03.08 16:33
Оценка:
Здравствуйте, GlebZ, Вы писали:
GZ>Здравствуйте, <Аноним>, Вы писали:
А>>Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого", т.е. не все N ждущих потоков будут освобожденны (или вообще ни один из них) когда "первый" поток вызвал Set? GZ>Может.
Хм.. Т.е. действия Set/Reset выведение "ждущих" потоков из ждущего состояния (WaitOne) операции не атомарные?
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, GlebZ, Вы писали:
GZ>>Здравствуйте, <Аноним>, Вы писали:
А>>>Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого", т.е. не все N ждущих потоков будут освобожденны (или вообще ни один из них) когда "первый" поток вызвал Set? GZ>>Может.
А>Хм.. Т.е. действия Set/Reset выведение "ждущих" потоков из ждущего состояния (WaitOne) операции не атомарные?
Нет. В той ситуации которой ты описал возможно что некоторые треды не успеют зарелизиться. Освобождение только одного потока гарантировано. Если у тебя не тысячи тредов, то достаточно перед резетом вставить Thread.Sleep(0) чтобы передать управление.
... << RSDN@Home 1.2.0 alpha rev. 789>>
Re[4]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
12.03.08 17:03
Оценка:
Здравствуйте, GlebZ, Вы писали:
А>>Хм.. Т.е. действия Set/Reset выведение "ждущих" потоков из ждущего состояния (WaitOne) операции не атомарные?
GZ>Нет. В той ситуации которой ты описал возможно что некоторые треды не успеют зарелизиться. Освобождение только одного потока гарантировано. Если у тебя не тысячи тредов, то достаточно перед резетом вставить Thread.Sleep(0) чтобы передать управление.
Здравствуйте, <Аноним>, Вы писали:
А>Ведь возможна следующая ситуация. Пусть N каких-то "других" потоков ждут на этом ивенте (ManualresetEvent.WaitOne). А>Пусть "первый" поток делает ManualResetEvent.Set чтобы "отпустить" все N ждущих потоков. А>А в следующий квант процессорного времени "второй" поток делает этуму же ивенту ManualResetEvent.Reset.
не проще ли AutoResetEvent использовать? Зачем гробить память и процессорное время, только для того чтобы сбрасывать событие?
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[2]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
12.03.08 17:32
Оценка:
Здравствуйте, _Morpheus_, Вы писали:
_M_>не проще ли AutoResetEvent использовать? Зачем гробить память и процессорное время, только для того чтобы сбрасывать событие?
Мне по Set надо освобождать не 1 поток, а все потоки которые могут ждать на этом ивенте.
Здравствуйте, <Аноним>, Вы писали:
_M_>>не проще ли AutoResetEvent использовать? Зачем гробить память и процессорное время, только для того чтобы сбрасывать событие?
А>Мне по Set надо освобождать не 1 поток, а все потоки которые могут ждать на этом ивенте.
специально для таких случаев предназначены методы Monitor.Pulse() и Monitor.PulseAll(), тебе в данном случае нужен Monitor.PulseAll()
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, GlebZ, Вы писали:
GZ>>Здравствуйте, <Аноним>, Вы писали:
А>>>Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого", т.е. не все N ждущих потоков будут освобожденны (или вообще ни один из них) когда "первый" поток вызвал Set? GZ>>Может.
А>Хм.. Т.е. действия Set/Reset выведение "ждущих" потоков из ждущего состояния (WaitOne) операции не атомарные?
атомарные, но если для потока который должен был проснуться не нашлось кванта времени, и event был сброшен обратно, то поток будет продолжать находиться в ожидании...
Здравствуйте, AndrewVK, Вы писали:
А>>Не получится ли так, что действие "второго" потока полностью или частично "отменит" действие "первого"
AVK>не получится.
получится, например если не нашлось кванта времени для потоков которые должны были проснуться за весь период, пока событие находилось во взведенном состоянии, то потоки так и не проснутся и будут продолжать ожидание...
вот пример, не смотря на то что событие было установлено и сброшено, это не приводит к тому чтобы потоки проснулись, т.к. за время пока событие было взведено у потоков небыло времени чтобы проснуться, а когда время появилось событие было уже сброшено:
using System;
using System.Threading;
public class Program
{
static void Main()
{
Program p = new Program();
p.Run();
}
private ManualResetEvent _event = new ManualResetEvent(false);
private AutoResetEvent _readyThread1 = new AutoResetEvent(false);
private AutoResetEvent _readyThread2 = new AutoResetEvent(false);
public void Run()
{
Thread t1 = new Thread(new ThreadStart(threadProc1));
t1.Name = "Thread1";
t1.IsBackground = true;
Thread t2 = new Thread(new ThreadStart(threadProc2));
t2.Name = "Thread1";
t2.IsBackground = true;
// переводим свой поток в очередь более главных потоков, чтобы время в первую очередь доставалось текущему потоку и в последнюю очередь, когда он добровольно отдаст управление, потокам t1 и t2...
t1.Priority = ThreadPriority.BelowNormal;
t2.Priority = ThreadPriority.BelowNormal;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
t1.Start();
t2.Start();
_readyThread1.WaitOne();
_readyThread2.WaitOne();
Console.WriteLine("Set/Reset...");
_event.Set();
_event.Reset();
// разрешаем делиться временем с потоками t1 и t2...
t1.Priority = ThreadPriority.Normal;
t2.Priority = ThreadPriority.Normal;
Thread.CurrentThread.Priority = ThreadPriority.Normal;
// событие было установлено и сброшено, но потоки не проснутся (если процессу доступно не более 1 процессора!)
Console.WriteLine("Wait for processing...");
Thread.Sleep(5000);
Console.WriteLine("Exit...");
}
private void threadProc1()
{
_readyThread1.Set();
_event.WaitOne();
Console.WriteLine("T1");
}
private void threadProc2()
{
_readyThread2.Set();
_event.WaitOne();
Console.WriteLine("T2");
}
}
если у вас многопроцессорная машина, то нужно прибить процесс только к одному процу...
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[4]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
12.03.08 18:14
Оценка:
Здравствуйте, _Morpheus_, Вы писали:
_M_>специально для таких случаев предназначены методы Monitor.Pulse() и Monitor.PulseAll(), тебе в данном случае нужен Monitor.PulseAll()
Не уверен что ты меня правильно понял.
Есть функция, которая может выполняться из разных потоков. Но одновременно должна — только в одном. Причем, если во время выполнения функции в каком-либо потоке, другой поток попытается ее запустить, ничего "лочится" не должно. Другой поток должен в этом случае просто дожаться выполнения функции в первом потоке. И все.
Вот как это организовано.
Если функция уже выполняется в одном из потоков, то при попытке ее вызова из другого, функция возвращает null.
Код, вызывающий эту функцию, проверяет, а не вернулся ли null. Если да, то он "вешается" на событие и ждет (ManualResetEvent.WaitOne). То что он будет ждать — гарантированно. Т.к. первым делом код функции делает ManualResetEvent.Reset.
Перед возвратом, функция делает ManualResetEvent.Set. Предполагается, что все ждущие потоки при этом выйдут из ожидания. При этом функцию выпонять они уже не будут (см. изначальное условие).
Но я опасаюсь вот чего. Если перед тем, как успеют проснуться все потоки (после ManualresetEvet.Set), функция будет вызвана еще раз (т.е. будет опять сделать ManualResetEvent.Reset), то у них и шанса-то проснуться не будет.
Как мне здеть может помочь Monitor и PulseAll, объясни плз поподробнее..
Здравствуйте, <Аноним>, Вы писали:
А>А если тысячи?..
А если тысячи — то ReaderWriterLock. Все ожидающие как ReaderLock. Основная WriterLock. Там вроде бы гарантируется.
Здравствуйте, <Аноним>, Вы писали:
_M_>>специально для таких случаев предназначены методы Monitor.Pulse() и Monitor.PulseAll(), тебе в данном случае нужен Monitor.PulseAll()
А>Не уверен что ты меня правильно понял.
А>Есть функция, которая может выполняться из разных потоков. Но одновременно должна — только в одном. Причем, если во время выполнения функции в каком-либо потоке, другой поток попытается ее запустить, ничего "лочится" не должно. Другой поток должен в этом случае просто дожаться выполнения функции в первом потоке. И все.
ничего лочиться не должно, причем нужно чтобы лочилось? это как?
То что вы описали — чистейшая функциональность lock'а
А>Вот как это организовано. А>Если функция уже выполняется в одном из потоков, то при попытке ее вызова из другого, функция возвращает null.
А>Но я опасаюсь вот чего. Если перед тем, как успеют проснуться все потоки (после ManualresetEvet.Set), функция будет вызвана еще раз (т.е. будет опять сделать ManualResetEvent.Reset), то у них и шанса-то проснуться не будет.
А>Как мне здеть может помочь Monitor и PulseAll, объясни плз поподробнее..
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re[6]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
13.03.08 10:09
Оценка:
Здравствуйте, GlebZ, Вы писали:
GZ>А если тысячи — то ReaderWriterLock. Все ожидающие как ReaderLock. Основная WriterLock. Там вроде бы гарантируется.
Спасибо, попробую разобраться.
Re[6]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
13.03.08 11:55
Оценка:
Здравствуйте, _Morpheus_, Вы писали:
_M_>ничего лочиться не должно, причем нужно чтобы лочилось? это как?
Это так, как я описал — ожидающие завершщения функцции потоки ждут не в операторе lock, а на ManualresetEvent.WaitOne.
_M_>То что вы описали — чистейшая функциональность lock'а
Частично (в функции) — да. Но повотрю еще раз — если функция выполняется одним потоком, и в это время вызвана другим потоком, он должен просто дождаться ее завершения. И ВСЕ. Из ждущего потока функция вызвываться не должна.
_M_>это можно делать так: _M_>
Частично это так и сделано. Только, как я уже говорил, если функция будет вызвана из другого потока во время ее выполения, этот поток не просто дложен получить null и успокоиться, а ждать на ManualResetEvent, который будет выставлен в Set cамой функцией по окончании своей работы.
Вы мне сказали, что это нужно делать при помощи Monitor.PulseAll, только вот его я здесь и не вижу
Здравствуйте, <Аноним>, Вы писали:
_M_>>ничего лочиться не должно, причем нужно чтобы лочилось? это как?
А>Это так, как я описал — ожидающие завершщения функцции потоки ждут не в операторе lock, а на ManualresetEvent.WaitOne.
lock и WaitOne() это одно и тоже — и то и другое отдает процессорное время системе для других потоков...
_M_>>То что вы описали — чистейшая функциональность lock'а
А>Частично (в функции) — да. Но повотрю еще раз — если функция выполняется одним потоком, и в это время вызвана другим потоком, он должен просто дождаться ее завершения. И ВСЕ. Из ждущего потока функция вызвываться не должна.
из ждущего потока функция вызываться не может по определению, т.к. ждущий поток спит — у него нет процессорного времени чтобы вызвать функцию...
А>Частично это так и сделано. Только, как я уже говорил, если функция будет вызвана из другого потока во время ее выполения, этот поток не просто дложен получить null и успокоиться, а ждать на ManualResetEvent, который будет выставлен в Set cамой функцией по окончании своей работы.
тогда вам такой подход не нужен, вам нужен обычный lock, без всяких возвратов null
А>Вы мне сказали, что это нужно делать при помощи Monitor.PulseAll, только вот его я здесь и не вижу
да точно также как с событием, только вместо вызова Set, вызывать нужно PulseAll — это аналог Set, Reset, но с той разницей что проснутся все потоки ожидающие освобождения заданного объекта
_M_>тогда вам такой подход не нужен, вам нужен обычный lock, без всяких возвратов null
И плюс lock в тысячу раз дешевле Manual/AutoResetEvent.
Re[9]: Конкурентный доступ к ManulaResetEvent
От:
Аноним
Дата:
14.03.08 08:48
Оценка:
Здравствуйте, mihailik, Вы писали:
_M_>>тогда вам такой подход не нужен, вам нужен обычный lock, без всяких возвратов null
M>И плюс lock в тысячу раз дешевле Manual/AutoResetEvent.
Я это понимаю.
Мне не нужно чтобы метод ПРОСТО не мог выполняться одновременно из нескольких потоков c одним НО (см. ниже).
Допустим есть метод MethodSynchorinzed(), который может вызываться кучей "других" методов.
Каждый из этих "других" методов может вызываться в отдельном потоке.
Мне нужно, что если метод MethodSynchronized() уже выполнятеся ОДИМ потоком, он не мог выполняться в других.
НО ПРИ ЭТОМ, "другие" методы, пытающиеся выхвать MethodSynchronized из других потоков, просто дождались выполнения MethodSynchronized()
И НЕ ЗАПУСКАЛИ его, т.е. просто сделали return.