Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Mutex


Автор: unReg 3.12.2003, 02:29
Здравствуйте. Нашел в вашем форуме топик о мьютексах, но он оказался не совсем про мьютексы. Непонятно действие некоторых функций (в аглицком не силен). Мне нужно было синхронизировать доступ к файлу со стороны разных приложений. Одно приложение (1) создает и постоянно обновляет файл, а другое(ие) (2) просто читает его содержимое. (1) создает мьютекс в момент своего создания, а (2) берет хендл созданного мьютекса с помощью OpenMutex. Работа с файлом начинается после WaitForSingleObject (стандартный вариант). По окончании работы с файлом делаю ReleaseMutex и в (1) и в (2). CloseHandle делаю по Destroy (1).
И вот что получается:
1: работа с файлом синхронизируется. (это хорошо)
2: как уже говорилось-счетчик дескрипторов неуклонно растет пока работает (2)! (это плохо)
3: если я делаю в (2) CloseHandle после работы с файлом, то (1) затыкается на WaitForSingleObject. (а это вообще противоречит 2-му пункту).

Проблему 2 и 3 сейчас решаю так: при каждом обращении к файлу из (2), вместо OpenMutex делаю CreateMutex, потом WaitForSingleObject, потом ReleaseMutex и CloseHandle.
Нафиг тогда OpenMutex? если как написано в справке The OpenMutex function returns a handle of an existing named mutex object. Или я че-то неправильно понимаю?

Автор: unReg 4.12.2003, 03:08
OK. Наверное без кода никто не ответит. Каюсь.
Код потока (1) который создает мьютекс (каждый поток работает со своим файлом и своим MutexName):
Код


type TCThread=class(TThread)
private
  hMutex:THandle;
  F:TFileStream;
.....
public
 constructor ....
 destructor  ....
end;


constructor TCThread.Create(MutexName,FileName:string;ItemId:integer;CtrlCnfg:PCnfg);
var ...
begin
inherited Create(true);
.....
   hMutex:=CreateMutex(nil,False,PAnsiChar(MutexName));
.....
end;

procedure TCThread.Execute;
begin
  While not Terminated do
  begin
      if WaitForSingleObject(hMutex,0)<>wait_TimeOut then
           begin
             if not FileExists(FileName) then
              AnsFile:=TFileStream.Create(FileName,fmCreate)
             else
             AnsFile:=TFileStream.Create(FileName,fmOpenReadWrite);
             AnsFile.Seek(0,soFromBeginning);
             AnsFile.Write(BuffOut^,BuffOutLength);
             AnsFile.Free;
             ReleaseMutex(hMutex);
  end;
end;

destructor TCThread.Destroy;
begin
 ......
 CloseHandle(hMutex);
 ......
inherited Destroy;
end;


Теперь код приложения которое читает файл (2) первый вариант:
Код

.....
var hMutex:THandle; {глобальное объявление}
     FileName,MutexName:string;

.....

procedure TfrmMain.Timer1Timer(Sender: TObject);
var Buff:array [0..848] of byte;
begin
 hMutex:=OpenMutex(MUTEX_ALL_ACCESS,false,PAnsiChar(MutexName));
 if waitforsingleobject(hMutex,0)<>wait_timeout then
 begin
   F:=TFileStream.Create(FileName,fmOpenRead);
   F.Read(Buff,849);
   F.Free;
 end;
 ReleaseMutex(hMutex);
end;

Вот при таком раскладе растет счетчик дескрипторов.
Если после ReleaseMutex(hMutex); написать CloseHandle(hMutex), то (1) затыкается на waitforsingleobject (таймаут);
А если код (2) написать вот так (второй вариант):
Код

  procedure TfrmMain.Timer1Timer(Sender: TObject);
var Buff:array [0..848] of byte;
begin
 hMutex:=CreateMutex(nil,false,PAnsiChar(MutexName));
 if waitforsingleobject(hMutex,0)<>wait_timeout then
 begin
   F:=TFileStream.Create(FName,fmOpenRead);
   F.Read(Buff,849);
   F.Free;
 end;
 ReleaseMutex(hMutex);
 CloseHandle(hMutex);
 end;

тогда счетчик не увеличивается, и все вроде бы нормально, но!
Обнаружилась еще одна странность. Я попробовал вывести значение hMutex в Caption формы (caption:=inttostr(hMutex)) и в первом варианте кода (2) выходили разные(!) значения при каждой отработке процедуры, а во втором варианте значения одинаковые(!). Мало того, во втором варианте я откомпилил приложение (2) в нескольких вариантах (каждому потоку (1) соответствовал вариант приложения (2), естественно имена мьютексов и файлов различались в каждом потоке) и опять получил одинаковые значения hMutex во всех(!) запущенных приложениях (2). Вопрос, собственно, вот в чем: почему не работает первый вариант кода и че за ерунда с хендлами?

Автор: Dmitry V.Abramov 4.12.2003, 17:46
Для начала вот это место:
Код
procedure TCThread.Execute;
begin
 While not Terminated do
 begin
     if WaitForSingleObject(hMutex,0)<>wait_TimeOut then
          begin
            if not FileExists(FileName) then
             AnsFile:=TFileStream.Create(FileName,fmCreate)
            else
            AnsFile:=TFileStream.Create(FileName,fmOpenReadWrite);
            AnsFile.Seek(0,soFromBeginning);
            AnsFile.Write(BuffOut^,BuffOutLength);
            AnsFile.Free;
            ReleaseMutex(hMutex);
 end;
end;


3 begin'a & 2 end'a - это нормально?

Автор: p0s0l 4.12.2003, 21:59
Попробуй в WaitForSingleObject вместо 0 поставить INFINITE, а так всё правильно, и в первом варианте второго кода нужно делать CloseHandle

Автор: Unregistered 5.12.2003, 02:09
Dmitry V.Abramov нашел к чему прицепиться smile.gif

p0s0l Спасибо, но флаг infinite на сколько я понял заставляет ждать до тех пор пока не получит сигнала?

Автор: Ars 5.12.2003, 18:35
Код
.....
var hMutex:THandle; {глобальное объявление}
    FileName,MutexName:string;

.....

procedure TfrmMain.Timer1Timer(Sender: TObject);
var Buff:array [0..848] of byte;
begin
hMutex:=OpenMutex(MUTEX_ALL_ACCESS,false,PAnsiChar(MutexName));
if waitforsingleobject(hMutex,0)<>wait_timeout then
begin
  F:=TFileStream.Create(FileName,fmOpenRead);
  F.Read(Buff,849);
  F.Free;
end;
ReleaseMutex(hMutex);
end;


Проблема в том, что при каждом вхождении ты получаешь новый дескриптор мьютекса и нигде его не освобождаешь. Поэтому у тебя всегда новое значение дескриптора и растет счетчик дескрипторов.
Я не понимаю, зачем вообще каждый раз получать дескриптор. Достаточно получить его однажды в начале работы приложения и хранить в данных TfrmMain или в глобальной переменной.

Функция OpenMutex, действительно, получает хэндл на существующий мьютекс, а если он не существует, то обламывается, в отличии от CreateMutex, которая или создает новый или открывает существующий (что именно, можно проверить последующим вызовом GetLastError). Tем самым устраняется необходимость последовательных вызовов OpenMutex, CreateMutex и проверок хэндлов для получения мьютекса. Кстати, у тебя нигде нет проверки на валидность хэндла...

Код
destructor TCThread.Destroy;
begin
......
ReleaseMutex(hMutex);       << Сюда неплохо бы вставить освобождение мьютекса
CloseHandle(hMutex);
......
inherited Destroy;
end;

Автор: Unregistered 8.12.2003, 01:55
Спасибо. Тут чего-то действительно фигню спорол (каждый раз открывыл мьютекс). Это явно ни к чему.
В любом случае, господа, вы мне очень помогли. Еще раз - спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)