EnterCriticalSection
ситуация такая, есть ресурс, есть несколько потоков(>2) претендующих на этот ресурс, простой пример:
#include <iostream>
#include <queue>
using namespace std;
queue<DWORD> cs_queue;
queue<DWORD> th_queue;
class Mng
{
public:
Mng()
{
InitializeCriticalSection(&m_CS);
}
~Mng()
{
DeleteCriticalSection(&m_CS);
}
void process()
{
EnterCriticalSection(&m_CS);
cs_queue.push(GetCurrentThreadId());
Sleep(200); // имитация какой-то работы
LeaveCriticalSection(&m_CS);
}
CRITICAL_SECTION m_CS;
};
Mng mng;
DWORD WINAPI thread(void * v)
{
th_queue.push(GetCurrentThreadId());
mng.process();
return 0;
}
int main()
{
DWORD dw1 = 0;
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
CreateThread(0, 0, thread, 0, 0, 0);
Sleep(4000); // ждем завершения всех потоков (можно через WaitForMultipleObjects)
cout << "threads queue" << endl;
while(!th_queue.empty())
{
cout << th_queue.front() << " ";
th_queue.pop();
}
cout << endl << "cs queue" << endl;
while(!cs_queue.empty())
{
cout << cs_queue.front() << " ";
cs_queue.pop();
}
getchar();
return 0;
}
в общем, запускаю просто программку, списки сходятся.
запускаю через F5 в студии, без всяких точек останова и т.д. - списки не сходятся.
Как работает EnterCriticalSection, и стоит ли изобретать велосипед:
- lock() - ставить потоки в очередь на ожидание(SuspendThread),
- unlock() - выбирать из очереди и размораживать их (ResumeThread)
?
заранее спасибо за любую помощь. :)
А то, что в каком-то случае работает, а в каком-то не работает - всего лишь последствие ошибки в программе. В таких случаях не следует привязываться к подобным фактам, и тем более, не искать ошибок в компиляторах и средах разработки. Необходимо сначала разобраться в своем коде.
p.s. почитайте что-нибудь по ключевым словам "STL Thread Safety", возможно, надежда была на безопасность со стороны STL...
Ничего подобного с Suspend и Resume творить не надо, шаблон замка прекрасно реализуется с помощью той же критической секции. Ну а если уж совсем не изобретать велосипед, то пользуйтесь boost.
cs_queue и th_queue я использую чтобы смотреть порядок вхождения потоков в секцию и выполнения кода.
вот здесь:
должна быть работа с каким-то общим ресурсом.
идея такова: потоки стали в очередь для выполнения функции process(), и по одному из этой очереди в ПРАВИЛЬНОМ ПОРЯДКЕ должны выходить и выполнять этот process().
я воспользовался критическими секциями для синхронизации. и у меня как-то нарушается порядок выполнерия - я написал..
хотелось бы не приплетать сторонние библиотеки...
{
th_queue.push(GetCurrentThreadId());
mng.process();
return 0;
}
MSDN: Thread Safety in the Standard C++ Library
The container classes are vector, deque, list, queue, stack , priority_queue, valarray, map, multimap, set, multiset, basic_string, bitset.
...
For writes to the same object, the object is thread safe for writing from one thread when no readers on other threads
...
а как мне организовать очередность потоков для ресурса?
эти очереди я сделал только чтобы сверить очередность вхождения и выхождения из секции, были кой-какие проблемы..
-
меня интересовало - СЕКЦИЯ не меняет последовательность претендующих потоков? дело в том что я встретил какую-то бурду про секцию и она меня жутко смутила.
:D
все это похоже на бред конечно, но в литературе ничего не написано про очередность потоков... а я сам я и не знаю у кого спросить.
эти очереди я сделал только чтобы сверить очередность вхождения и выхождения из секции, были кой-какие проблемы..
-
меня интересовало - СЕКЦИЯ не меняет последовательность претендующих потоков? дело в том что я встретил какую-то бурду про секцию и она меня жутко смутила.
:D
все это похоже на бред конечно, но в литературе ничего не написано про очередность потоков... а я сам я и не знаю у кого спросить.
А какая разница меняет она очередность или нет?
Задача критической секции - обеспечить выполнение участка кода только одним потоком. В каком порядке будут заходить потоки должно быть абсолютно не важным.
собственно, прежде чем биться головой об написание какого-то своего метода синхронизации, хотелось узнать может в системе такое уже есть? :)
поток1 захватил секцию и выполняется(долго).
через некоторое время поток2 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток3 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток4 подготовил данные и ожидает в EnterCriticalSection()
поток1 освобождает секцию, в вместо потока2 в защищенную область попадает поток4.
вот чтобы такого не было... секции гарантируют правильную последовательность?
Что касается общего ресурса:
Ваша задача - обеспечить безопасную работу с некоторым ресурсом.
Ресурс ваш можно инкапсулировать в некий класс и обеспечить интерфейс для безопасного взаимодействия с ним. В реализации интерфейса делайте синхронизацию там, где это необходимо.
поток1 захватил секцию и выполняется(долго).
через некоторое время поток2 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток3 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток4 подготовил данные и ожидает в EnterCriticalSection()
поток1 освобождает секцию, в вместо потока2 в защищенную область попадает поток4.
вот чтобы такого не было... секции гарантируют правильную последовательность?
Зачем вам эта гарантия, когда нет гарантии, что потоки встанут в очередь на вход в секцию в определенном порядке? Может будет так:
поток4 захватил секцию и выполняется(долго).
через некоторое время поток1 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток3 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток2 подготовил данные и ожидает в EnterCriticalSection()
поток1 захватил секцию и выполняется(долго).
через некоторое время поток2 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток3 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток4 подготовил данные и ожидает в EnterCriticalSection()
поток1 освобождает секцию, в вместо потока2 в защищенную область попадает поток4.
вот чтобы такого не было... секции гарантируют правильную последовательность?
Нет не гарантируют. Именно для этого в ОС и существуют объекты синхронизации позволяющие упорядочивать доступ к общим ресурсам. Секции могут гарантировать что к ресурсу получает достут только один из потоков - последовательность доступа гарантировать они не могут.
не совсем. код потоков разный, ресурс - например глобальный объект, какого-нибудь класса.
--
я создал базовый класс с методами lock() и unlock() - традиционно :)
--
отказаться от выполнения последовательности не могу - условие.
--
как реализовать такую синхронизацию потоков? пусть даже без критических секций?
пример неплохо бы...
InterlockedPushEntrySList и т.д.?
поток4 захватил секцию и выполняется(долго).
через некоторое время поток1 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток3 подготовил данные и ожидает в EnterCriticalSection()
через некоторое время поток2 подготовил данные и ожидает в EnterCriticalSection()
да может будет и так, но пользоваться ресурсом они должны в той последовательности, в которой они стали в очередь на него.
1. если выполняется поток1, то выполнить код, установить сигнал1
2. если выполняется поток2, то ждать сигнал1, выполнить код, установить сигнал2
3. если выполняется поток3, то ждать сигнал2, выполнить код, установить сигнал3
...
Сигнал можно реализовать скажем событием.
-
потоки реализуют работают с бд. поэтому последовательность важна. :)
-
я сделал так:
есть флаг зянят/свободен.
поток входит в lock() - проверяется флаг,
если свободен - ставим занятым, продолжаем выполнение. если занят - поток ставится себя в очередь, и вызывается SuspendThread для себя.(засыпает на этом месте)
поток входит в unlock() - поток смотрит есть ли в очереди замороженных потоков кто-либо. если есть - достаем его и вызывает ResumeThread для него. если очередь пуста - ставим флаг - свободен.
_
классический алгоритм вроде. :)
но deadlock'и случаются постоянно. (т.к. очередь тоже нужно синхронизировать и т.д.)
_
ЧТО ДЕЛАТЬ?
0.5 сбор потоков в точке
1. если выполняется поток1, то выполнить код, покинуть секцию1
2. если выполняется поток2, то войти в секцию1, выполнить код, покинуть секцию2
3. если выполняется поток3, то войти в секцию2, выполнить код, покинуть секцию3
если выполняется поток1, то выполнить код, покинуть секцию1
а если в этот момент на секцию 1 станет второй поток и будет ждать, дождется и полезет выполнять. нет?
можно пожалуйста, хоть схематично программу набросать?
если выполняется поток1, то выполнить код, покинуть секцию1
а если в этот момент на секцию 1 станет второй поток и будет ждать, дождется и полезет выполнять. нет? а если третий станет в ожидание, а если четвертый, и все на секции1 :D а где гарантия что секция их отпустит так как они стали? :)
если выполняется поток1, то выполнить код, покинуть секцию1
а если в этот момент на секцию 1 станет второй поток и будет ждать, дождется и полезет выполнять. нет? а если третий станет в ожидание, а если четвертый, и все на секции1 :D а где гарантия что секция их отпустит так как они стали? :)
Со вторым потоком все правильно, это же и требуется? Первый выполняет задачу, после ее завершения выполняется второй поток.
А третий станет не на секция1, а на секция2, которая освободиться только после завершения работы потока2. Аналогично с 4-й, 5-й. Я просто смоделировал сигнал критической секцией, то же самое можно сделать событием, защелкой...
Подправил схему, не совсем конечно понятно сформулировал
если не сильно занят, пожалуйста, набросай код хоть какой-нибудь.
а если будут. что их остановит? :D
все равно большое спасибо за идею. :)
if (thread == 3) Enter(section2)
if (thread == 4) Enter(section3)
<выполняем код>
if (thread == 1) Leave(section1)
if (thread == 2) Leave(section2)
if (thread == 3) Leave(section3)
потом пришел поток thread = 3, проверились условия и он спокойно вошел в секцию section2, и полез к данным, которые еще заняты потоком thread = 2. разве нет?
_
секции: ОДНА секция блокирует НЕСКОЛЬКО потоков(кроме одного), если эти НЕСКОЛЬКО потоков захотят в нее войти.
в этом алгоритме потоки вообще не пересекаются на одной секции.
КАК блокирует? Enter во все секции? освобождение опять же может быть в проивзольном порядке? :)
синхронизация синхронизации :)
пожалуйста, если можно, весь код :)
Что мы имеем - ресурс, к которому по_определенной_очереди должен выполнятся доступ.
То есть имеем 1(один) поток (назовем его П1) и последовательность действий, с приоритетами, которые П1 должен выполнить. Значит так и делаем - в std::set(чтоб отсортировано) твои потоки пихают номер действия (через критсекцию) и выставляют евент "появился новый запрос". П1 оживает от евента, и выбирает (через критсекцию) номер первого действия из сета (как помним, они у нас упорядоченные). И выполняет его.
Все.
А ты пытаешся прикрутить 5е колесо к лодке.
атомарные операции - группа ф-ций Interlocked... (InterlockedIncrement etc)
Что мы имеем - ресурс, к которому по_определенной_очереди должен выполнятся доступ.
То есть имеем 1(один) поток (назовем его П1) и последовательность действий, с приоритетами, которые П1 должен выполнить. Значит так и делаем - в std::set(чтоб отсортировано) твои потоки пихают номер действия (через критсекцию) и выставляют евент "появился новый запрос". П1 оживает от евента, и выбирает (через критсекцию) номер первого действия из сета (как помним, они у нас упорядоченные). И выполняет его.
Все.
А ты пытаешся прикрутить 5е колесо к лодке.
претендующих на ресурс потоков может быть до нескольких сотен. и КАЖДЫЙ поток хочет выполнить только ОДНО действие над общим ресурсом.(потоки должны выполняться в правильной последовательности. :)) нужно создать один управляющий поток что ли? пожалуйста можно подробнее?
атомарные операции - группа ф-ций Interlocked... (InterlockedIncrement etc)
это я знаю. алгоритм CAS - CompareAndSwap, функции InterlockedCompareExchange. я про то что нигде не мог найти примера реализации :(
asm lock xchg eax,ebx
претендующих на ресурс потоков может быть до нескольких сотен. и КАЖДЫЙ поток хочет выполнить только ОДНО действие над общим ресурсом.(потоки должны выполняться в правильной последовательности. ) нужно создать один управляющий поток что ли?
Да хоть 2к. Да, создавай один рабочий поток.
Потоки только делают запрос на выполнение работы(потоки - клиенты). Саму работу делает один поток(поток - сервер). Он же может и выставлять эвент про окончание работы, чтоб клиенты, если им это нужно, знали когда выполнился их запрос.
Что конкретно непонятно?