我正在尝试使用共享功能(在主线程中)并使用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的评论中的建议删除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仅用于示例目的。
答案 0 :(得分:3)
将Enter()
和Leave()
的调用放在共享函数中是很好的,甚至是可取的。但是,如果您要使用try/__finally
,则需要将Enter()
放在try
之外,以防它失败。您不希望Leave()
您未成功Enter()
的内容,例如:
void __fastcall TForm1::SharedFunction(UnicodeString TextData)
{
csShared->Enter();
try
{
//...
Sleep(2000);
}
__finally
{
csShared->Leave();
}
}
void __fastcall TForm1::IdThreadComponent1Run(TIdThreadComponent *Sender)
{
SharedFunction("Thread 1 calling");
IdThreadComponent1->Stop();
}
由于您仍在使用Boost,因此您应该使用自己的mutex
和lock
类,然后您不必担心try/__finally
,{{1} },或Enter()
,例如:
Leave()
对于#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/locks.hpp>
boost::recursive_mutex mutex;
void __fastcall TForm1::SharedFunction(UnicodeString TextData)
{
boost::lock_guard<boost::recursive_mutex> lock(mutex);
//...
Sleep(2000);
}
访问,使用TMemo
或TIdSync
类以线程安全的方式执行该代码,例如:
TIdNotify
答案 1 :(得分:2)
我正在尝试利用共享功能(在主线程中)并从3个线程中使用它。
方法或过程(通常是一段代码)不属于线程本身。任何代码都可以从应用程序中的任何线程调用,并且如果以并发方式从不同的线程调用,它可以同时运行多次。
例如
procedure A();
begin
//do some work.
end;
你可以这样执行:
main thread
|
SomeFunc();
|
| spawns
| thread X
|---------|
| |
| |
| OtherF()
A() |
| | spawns thread Y
| |-------------|
| A() |
| | |
| | A()
| | |
| | |
t1>| | |
| | |
A returns | |
B() | |
| | |
t2>| | |
| | |
| A returns |
| thread end |
| |
| |
t3>| |
| A returns
| thread end
|
program end
在t1,3个不同的线程正在运行函数A(),在t2,2个线程仍在运行它(X和Y),在t3只有一个线程执行该函数(线程Y)。
调用csShared-&gt; Enter();在像上面这样的共享函数内部,或者我必须在每个线程中调用它,如下所示:
这取决于你。您必须定义调用它的位置,因为您有责任定义哪些代码片段必须仅在一个线程的上下文中运行,而其他代码将等待此操作完成以启动(串行执行)。
记住每个CriticalSection都是瓶颈。故意的,但你必须谨慎使用它,以避免在不需要的地方引入等待。