Доступ к файлам

 

Ндааа, шестой год уже программирую на MQL4, а иной раз голову поломать приходится конкретно. И кажется вроде знаешь уже язык - не должно быть сюрпризов. Ан нет - не тут то было.

Пишем скрипт, который открывает файл, записывает в него строку (как доказательство что файл реально был открыт), спит 15 секунд (этого времени нам хватит вполне для дальнейшего эксперимента), затем закрывает файл и и выдаёт сообщение, что удержание файла в открытом состоянии закончено.

//+------------------------------------------------------------------+
//|                                                     OpenFile.mq4 |
//|                                           Copyright 2012, DRKNN. |
//|                                                    drknn@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012,  DRKNN."
#property link      "drknn@mail.ru"

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start(){
  string FlName="1.txt";
  int file_handle;
  file_handle=FileOpen(FlName, FILE_CSV|FILE_READ|FILE_WRITE, " ");
  if (file_handle>0){
                FileSeek(file_handle,0,SEEK_END);
                if(FileWrite(file_handle,"добавленная строка")<0){
                  Alert(" ============================================== ");
                  Alert("Ошибка № ",GetLastError()," при попытке добавить строку в файл");
                }
                else{
                 Sleep(15000);
                }
        }
        else{
          Alert(" ============================================== ");
          Alert("Ошибка ",GetLastError()," - Не удалось открыть файл ",FlName);
        }
  FileClose(file_handle);
  Alert(" ============================================== ");
  Alert("Удержание файла окончено");
//----
   return(0);
  }
//+------------------------------------------------------------------+

Теперь, если в момент, пока скрипт спит и удерживает файл в открытом состоянии попытаться открыть для записи этот же файл из другого скрипта, то по логике вещей, терминал должен отреагировать на это отрицательно (ну чтоб не получилось, что пока один советник что-то пишет в файл, другой перехватывает инициативу, быстренько дописывает и сматывается. в результате первый советник допишет своё, сохранится и запись второго советника исчезнет). Проверяем этот момент. Пишем ДЛЛ-ку которая проверяет занятость файлов:

library basic;

uses
  SysUtils,Windows,Classes,Dialogs;

{$R *.res}

// ------------ Проверка занятости файла ------------------
function FileStatus(const FileName: string): Boolean; stdcall;
var
  F: TFileStream;
begin
{
    Значение             Описание
    fmCreate             Созда?т файл с данным именем. Если файл существует, то открыть его в
                                 режиме записи.
    fmOpenRead           Открыть файл только для чтения.
    fmOpenWrite          Открыть файл только на запись. При этом запись в файл заменит вс? его
                                 содержимое.
    fmOpenReadWrite Открыть файл скорее для изменения содержимого чем для замены его.

    Режим доступа должен иметь одно из следующих значений:

    Значение            Описание
    fmShareCompat       Доступ к файлу совместим с FCB.
    fmShareExclusive    Другое приложение не может открыть файл для различных целей.
    fmShareDenyWrite    Другое приложение может открыть файл для чтения, но не для записи.
    fmShareDenyRead     Другое приложение может открыть файл для записи, но не для чтения.
    fmShareDenyNone     Разрешить другим файлам делать с файлом и чтени и запись.

    Если файл невозможно открыть, то Create сгенерирует исключение.
    Возвращает true если файл не заблокирован
}
  try
    F := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive);
    try
      Result := true;
    finally
      F.Free;
    end;
  except
    Result := false;
  end;
end;


exports FileStatus;

begin
end.

Теперь пишем скрипт для проверки занятости файла:

//+------------------------------------------------------------------+
//|                                                 SkolkoZanjat.mq4 |
//|                                           Copyright 2012, DRKNN. |
//|                                                    drknn@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net"
#property show_inputs
#include <FileStatus_DLL.mqh>
string ScriptName=" SkolkoZanjat";
extern string FileName="1.txt";

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start(){

   string Otkuda=TerminalPath()+"\\experts\\files\\"+FileName;
   
   if(!FileStatus(Otkuda)){
      Alert("Файл открыт другой программой");
   }
   else{
     Alert("Файл свободен"); 
   }

   return(0);
  }
//+------------------------------------------------------------------+

А теперь открываем терминал и в папке файлов создаём документ 1.txt. Далее в терминале кидаем скрипт "OpenFile" на график и тут же кидаем на другой график скрипт "SkolkoZanjat". Опа, скрипт выдаёт информацию, что файл свободен. Это значит, что сие может стать источником не малых бед, если советники не используют между собой семафор доступа.

Разработчики, добавьте пожалуйста в справку метаэдитора информацию, что если файл открыт функцией FileOpen(), то другой советник (индикатор/скрипт) имеет право открыть его же и пока первый копается, внести в него изменения.

Файлы:
experts_1.zip  192 kb
 
Если в этой проблеме поискать выгоду, то получается, что несколько скриптов (индикаторов, советников) могут параллельно писать информацию в один файл, что может понадобиться, например, при распределенном сборе информации для последующего анализа.
 

По моему тут нет ошибки. По умолчанию есть общий доступ к файлу без ограничений.

Пользователь сам должен решить, как синхронизировать доступ.

 
Zhunko:

По моему тут нет ошибки. По умолчанию есть общий доступ к файлу без ограничений.

Ну ошибкой это сложно назвать. Но обычное умолчание это разрешение совместного доступа при чтении и запрещение при записи.

А еще фишка в том, что нельзя убрать совместный доступ вообще, а это уже проблема. В стандартных средствах MQL нет никакого подобия семафора, посему вероятность коллизии всегда существует.

 
granit77:
Если в этой проблеме поискать выгоду, то получается, что несколько скриптов (индикаторов, советников) могут параллельно писать информацию в один файл, что может понадобиться, например, при распределенном сборе информации для последующего анализа.
Думаю что не могут, но это можно проверить экспериментом, малость подправив мой скрипт "SkolkoZanjat". Я вижу это так: первый скрипт открывает файл. Поскольку доступ к файлу не блокируется, то как пить дать, он просто копирует его либо во временную область, либо во временный файл и работает уже с ним. Далее второй скрипт в это время вносит изменения в свою копию файла и сохраняется. Пусть он внёс число пять. После этого первый скрипт заканчивает вносить изменения в открытую им копию файла и сохраняется тоже. Фактчески, он свой временный файл записывает вместо существующего. Всё, нашей пятёрки в документе нет - есть только инфа первого скрипта.
 
TheXpert:

Ну ошибкой это сложно назвать. Но обычное умолчание это разрешение совместного доступа при чтении и запрещение при записи.

А еще фишка в том, что нельзя убрать совместный доступ вообще, а это уже проблема. В стандартных средствах MQL нет никакого подобия семафора, посему вероятность коллизии всегда существует.

Синхронизацию можно через глобальные переменные сделать.
 
Zhunko:
Синхронизацию можно через глобальные переменные сделать.
Нельзя. Мы уже говорили об этом. Заново начинать не собираюсь.
 
TheXpert:

Ну ошибкой это сложно назвать. Но обычное умолчание это разрешение совместного доступа при чтении и запрещение при записи.

А еще фишка в том, что нельзя убрать совместный доступ вообще, а это уже проблема. В стандартных средствах MQL нет никакого подобия семафора, посему вероятность коллизии всегда существует.


Семафор всегда можно сделать. Но коллизии могут быть всегда
 
TheXpert:
Нельзя. Мы уже говорили об этом. Заново начинать не собираюсь.

Можно. Даже с обработкой ошибок коллизий. Только на фиг это надо. Атомарно не получится. Будет слишком наворочено. Проще на уровне ядра критическими секция ми, мьютексами и т.п. в С++.

Хотя, можно, как-то WinAPI-функциями синхронизации в MQL4 попробывать.

 
bool GlobalVariableSetOnCondition( string name, double value, double check_value) 

Устанавливает новое значение существующей глобальной переменной, если текущее значение переменной равно значению третьего параметра check_value. Если переменной не существует, функция сгенерирует ошибку ERR_GLOBAL_VARIABLE_NOT_FOUND (4058) и вернет FALSE. При успешном выполнении функция возвращает TRUE, иначе FALSE. Для того, чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError(). Если текущее значение глобальной переменной отличается от check_value, функция вернет FALSE.
Функция обеспечивает атомарный доступ к глобальной переменной, поэтому она может быть использована для организации семафора при взаимодействии нескольких одновременно работающих экспертов в пределах одного клиентского терминала.

 
Эта функция действительно позволяет организовать семафор.

 
Офигеть! Ни разу не пользовался.