Расскажем о способе организации «критической секции» в советнике.
Дело в том, что если в момент выполнения этих функций будет осуществлен доступ к массивам данных, хранимых в глобальных переменных, из нескольких советников одновременно, то результат будет непредсказуемым и в большинстве случаев данные будут испорчены.
Для того, чтобы избежать этого, мы создадим объект «критическая секция», который может находиться в двух состояниях:
- «зеленый свет» (-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);
}
}