【发布时间】:2012-12-03 22:00:31
【问题描述】:
我正在尝试利用共享函数(在主线程中)并从 3 个线程中使用它。该函数将执行一些可能冗长的操作,例如磁盘写入,为了避免可能出现的问题,我将其锁定。我使用 Indy IdThreadComponent 和 TCriticalSection。这是它的外观:
//---------------------------------------------------------------------------
// In header file
//---------------------------------------------------------------------------
boost::scoped_ptr<TCriticalSection> csShared;
//---------------------------------------------------------------------------
// Main file
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
csShared.reset(new TCriticalSection);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::SharedFunction(UnicodeString TextData)
{
try
{
csShared->Enter(); // As suggested by Remy this is placed incorrectly and needs to be moved outside of try block
//Memo1->Lines->Add(TextData); // [EDIT] calling this within thread is wrong
Sleep(2000);
}
__finally
{
csShared->Leave();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdThreadComponent1Run(TIdThreadComponent *Sender)
{
SharedFunction("Thread 1 calling");
IdThreadComponent1->Stop();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdThreadComponent2Run(TIdThreadComponent *Sender)
{
SharedFunction("Thread 2 calling");
IdThreadComponent2->Stop();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::IdThreadComponent3Run(TIdThreadComponent *Sender)
{
SharedFunction("Thread 3 calling");
IdThreadComponent3->Stop();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IdThreadComponent1->Start();
IdThreadComponent2->Start();
IdThreadComponent3->Start();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
// Note - these 3 Stop() calls are used if threads are set to run infinitely
// But in this example it is not needed as they stop themselves
//IdThreadComponent1->Stop();
//IdThreadComponent2->Stop();
//IdThreadComponent3->Stop();
// Now wait for lock to be released [WRONG - COMMENTED IN EDIT]
//while (!csShared->TryEnter())
// {
// Sleep(500);
// }
//csShared->Leave();
// [EDIT v1] easier and faster way to wait than above
//csShared->Enter();
//csShared->Leave();
// [EDIT v2] block exit until all threads are done
while (IdThreadComponent1->Active || IdThreadComponent2->Active || IdThreadComponent3->Active)
{
Sleep(200); // make wait loop less CPU intensive
};
CanClose = true;
}
//---------------------------------------------------------------------------
问题:
- 如果我快速关闭窗口(只有一个线程执行该函数,它永远不会离开程序 - 永远等待,并且在调试器中只有第一个线程退出,其他两个不退出)。我正在使用 OnCloseQuery 事件来检查线程是否已完成。我做错了什么?
[编辑] 按照 David Heffernan 在 cmets 中的建议删除 Memo1->Lines->Add(TextData); 后,它会正确退出,因此这部分问题得到解决,以下内容仍然存在:
-
可以像上面那样在共享函数内部调用
csShared->Enter();,还是必须像这样在每个线程的外部调用它:void __fastcall TForm1::IdThreadComponent1Run(TIdThreadComponent *Sender) { csShared->Enter(); SharedFunction("Thread 1 calling"); csShared->Leave(); IdThreadComponent1->Stop(); } 这是否比上面的版本更好(在函数本身内调用
csShared->Enter();)?还是一样?两个版本似乎都可以正常工作,我想知道有什么区别,因为第一个更干净。
如果您想知道,我不需要Synchronize,这将用于磁盘写入而不是用于更新 VCL,因此上述 SharedFunction 仅用于示例目的。
【问题讨论】:
-
你知道你不能在 GUI 线程之外做 GUI 工作吗?您的程序似乎违反了该规则。
-
假设我没有在那个函数中放入备忘录中的东西。共享函数只是一个示例,但实际上它会写入磁盘并且可能很长。
-
TryEnter 周围的循环是一场灾难。只需调用 Enter。这将一直阻塞,直到获得锁为止。
-
不,它不起作用。在线程完成之前它不会阻塞。它只是获取然后释放一个锁。如果你想阻塞直到发生某些事情,你需要一个等待函数。
-
这仍然不是一个很好的等待函数,因为它会以 100% CPU 旋转。
标签: multithreading delphi c++builder indy