Почему метод TMutex Acquire () не блокирует мьютекс?

Пока у меня есть этот код:

****SimpleForm.h****
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TMemo *Memo1;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
private:    // User declarations
    TMutex *mtx;
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};

****SimpleForm.cpp****
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    mtx = new TMutex(true);
    WorkerThread *wt = new WorkerThread(false, mtx);
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    mtx->Acquire();
        Memo1->Lines->Add("Locked...");
    mtx->Release();
}

****WorkerThread.h****
class WorkerThread : public TThread
{
private:
    TMutex *mtx;
protected:
    void __fastcall Execute();
public:
    __fastcall WorkerThread(bool CreateSuspended, TMutex *mtx);
    void __fastcall CheckLock();
};
****WorkerThread.cpp****
__fastcall WorkerThread::WorkerThread(bool CreateSuspended, TMutex *mtx)
    : TThread(CreateSuspended)
{
    this->mtx = mtx;
}

void __fastcall WorkerThread::Execute()
{
    while(true){
        Sleep(1000);
        Synchronize(CheckLock);
    }

}

void __fastcall WorkerThread::CheckLock(){
    this->mtx->Acquire();
    Form1->Memo1->Lines->Add("Locked from thread");
    //this->mtx->Release();
}

Проблема в том, что mtx->Acquire() не блокирует мьютекс, когда я комментирую mtx->Release(), во время выполнения ничего не меняется, оба потока могут обращаться к одному и тому же общему ресурсу одновременно, ведьма - это не то, что я хочу. Я использовал p_threads в среде Linux, и когда мьютекс заблокирован, другие потоки ждут, когда он станет доступным. Как добиться того же результата с помощью C ++ CodeGear 2009?


person Hitman_99    schedule 13.10.2011    source источник


Ответы (1)


Что-то еще является объяснением вашей проблемы, потому что TMutex::Acquire действительно блокирует объект мьютекса. Реализация TMutex выглядит так:

procedure TMutex.Acquire;
begin
  if WaitFor(INFINITE) = wrError then
    RaiseLastOSError;
end;

procedure TMutex.Release;
begin
  if not ReleaseMutex(FHandle) then
    RaiseLastOSError;
end;

И WaitFor вызывает WaitForMultipleObjectsEx, передавая дескриптор мьютекса.

Скорее всего, у вас действительно есть несколько мьютексов, но я не могу сказать наверняка, так как не могу видеть весь ваш код.

Наконец, для синхронизации процессов вам следует предпочесть критическую секцию Windows, которая работает лучше, чем объект мьютекса Windows. Это TCriticalSection в RTL.


После вашего обновления легко увидеть, что происходит. Все использование блокировки происходит в основном потоке. Вы вызываете Synchronize, в результате чего метод выполняется в основном потоке. Если вы вызываете CheckLock непосредственно из своего Execute метода, вы попадете в тупик, как и предполагалось.

Вам нужно использовать Synchronize для всех вызовов графического интерфейса. В целом он работает, сигнализируя основному потоку о том, что в очереди синхронизации есть что-то, а затем ожидая, пока основной поток завершит свою работу. Это асинхронный метод.

person David Heffernan    schedule 13.10.2011
comment
Просто протестировал его с помощью TCriticalSection, результаты такие же. Я проверил, и объекты TMutext / TCriticalSection одинаковы в обоих потоках, но почему-то я не могу их заблокировать. - person Hitman_99; 13.10.2011
comment
Они блокируются. Я использую эти предметы изо дня в день. Есть кое-что еще, чего я не вижу. - person David Heffernan; 13.10.2011
comment
Я отредактировал исходный фрагмент кода. Теперь полный код есть. Обратите внимание на закомментированный mtx- ›Release (); строка, почему основной поток все еще может успешно заблокировать мьютекс? - person Hitman_99; 13.10.2011
comment
Смотрите мое обновление. Я решил это сейчас. Нужен дополнительный код! - person David Heffernan; 13.10.2011
comment
Это работает, потому что мьютекс изначально заблокирован основным потоком, а затем рабочий поток синхронизирует свои обновления пользовательского интерфейса с основным потоком, поэтому основной поток просто повторно входит в блокировку, которая уже есть. Поскольку вы используете Sychoronize(), просто избавьтесь от мьютекса, поскольку в вашем примере он совершенно бесполезен. - person Remy Lebeau; 18.10.2011