"Критическая секция": разграничение доступа к ресурсу
В этом выпуске я расскажу о способе организации "критической секции" в советнике.
Дело в том, что если в момент выполнения этих функций будет осуществлен доступ к массивам данных, хранимых в глобальных переменных, из нескольких советников одновременно, то результат будет непредсказуемым и в большинстве случаев данные будут испорчены.
Для того, чтобы избежать этого, мы создадим объект "критическая секция", который может находиться в двух состояниях:
- "зеленый свет" (-1); и
- "красный свет" (1).
В каждый конкретный момент только один советник может получить доступ к данным. Пока советник получает данные, будет гореть "красный свет" и другие советники будут ждать "зеленового света". Если горит "зеленый свет", то дорога свободна, т.к. никто в данный момент к данным не обращается.
Напишем две функции:
- Lock() - вызываем ее перед началом работы с ресурсом. Функция ждет "зеленового света" и меняет его на "красный".
- Unlock() - обязательно вызываем ее после окончания работы с ресурсом, чтобы снова зажечь "зеленый свет".
Вот исходный код этих функций:
//+------------------------------------------------------------------+
//| Lock() |
//| |
//| Возвращает: |
//| 0 - если "критическая секция" успешно |
//| заблокирована |
//| 1 - в случае ошибки |
//| 2 - эксперт остановлен |
//| 3 - по таймауту (слишком долго ждали) |
//+------------------------------------------------------------------+
int Lock(string GlobVarName, int timeout = 0)
{
string critical_section = GlobVarName+"Lock";
// проверим, существует ли переменная critical_section
if (!GlobalVariableCheck(critical_section))
{
if (GetLastError()!=0) return(1);
// переменная не существует, создадим ее
if (GlobalVariableSet(critical_section, -1.0)==0) return(1);
// переменная создана
}
int StartTime = GetTickCount();
// ждем "зеленового света"
while (true)
{
// проверить, не загорелся ли "зеленый свет"
if (GlobalVariableGet(critical_section)==-1.0)
{
// "зеленый свет" загорелся, зажигаем "красный свет"
if (GlobalVariableSetOnCondition(critical_section, 1.0, -1.0)) return(0);
// нас опередили, поэтому ждем "зеленового света"
}
// проверим, не остановлен ли эксперт
if (IsStopped()) return(2);
// таймаут не истек?
if (timeout!=0)
{
if ((GetTickCount()-StartTime)>timeout*1000) return(3);
}
// спим 0.1 секунды
Sleep(100);
}
}
//+------------------------------------------------------------------+
//| Unlock() |
//| |
//| Возвращает: |
//| 0 - если "критическая секция" успешно |
//| разблокирована |
//| 1 - в случае ошибки |
//| 2 - эксперт остановлен |
//| 3 - по таймауту (слишком долго ждали) |
//+------------------------------------------------------------------+
int Unlock(string GlobVarName, int timeout = 0)
{
string critical_section = GlobVarName+"Lock";
// проверим, существует ли переменная critical_section
if (!GlobalVariableCheck(critical_section))
{
if (GetLastError()!=0) return(1);
// переменная не существует, создадим ее
if (GlobalVariableSet(critical_section, -1.0)==0) return(1);
// переменная создана, поэтому выходим
return(-1.0);
}
int StartTime = GetTickCount();
// бесконечный цикл
while (true)
{
// пытаемся установить "зеленый свет"
if (GlobalVariableSetOnCondition(critical_section, -1.0, 1.0)) return(0);
// проверим, не остановлен ли эксперт
if (IsStopped()) return(2);
// таймаут не истек?
if (timeout!=0)
{
if ((GetTickCount()-StartTime)>timeout*1000) return(3);
}
// спим 0.1 секунды
Sleep(100);
}
}
В следующем выпуске я разберу код этих функций.
Все статьи по теме "Пишем советников для MetaTrader 4".
- Механическая торговая система - миф или реальность?
- С чего начать при написании советника:
- Создаем нового советника - Настраиваем параметры. - Язык MetaQuotes Language 4: