我想在WinXP SP3上使用Borland的C++ Builder 6编写一个多线程的图形处理程序,但已经遇到(我认为)同步问题,并且找不到原因。多线程同步
主窗体(Form1)具有从文件加载的TPicture。这个副本由线程通过Synchronize()调用获得,并且工作正常。该线程对图像做了一些处理,理论上它会定期更新主窗体图像。主窗体也控制一台机器,并且是“第一度假村”紧急停止,所以阻止不是一种选择。一切都很好,直到主窗体获得工作副本或工作副本的副本(对不起,但它已经到了),程序挂起,并且只响应IDE中的“程序重置” 。一个糟糕的解决方案是将工作映像复制到剪贴板,然后从主窗体中,从剪贴板复制到主窗体的图像。
//Synchronization routines:
//----------------------------------------------------------------
`void __fastcall ImageRout::update()
{
Form1->Image9->Picture->Bitmap->Assign(Imgcopy);
//never returns
}
//----------------------------------------------------------------
void __fastcall ImageRout::getimage()
{
Imgcopy->Assign(Form1->Image9->Picture);
}
//----------------------------------------------------------------
//do the initialisation things... Then,
//(data is a struct, loaded with image data via a Synchronize() call)
Imgcopy=new Graphics::TBitmap;
Imgcopy->Width=data.width;
Imgcopy->Height=data.height; //size the bitmap
while(Imgcopy->Canvas->LockCount!=1)
{
Imgcopy->Canvas->TryLock();
} //have to Lock() the image or it gets lost... Somewhere
Synchronize(getimage); //works fine
//do some work on Imgcopy
//"By the book"- attempt 1
//(rate (=15) is a 'brake' to stop every alteration being displayed)
update_count++;
if(update_count>rate) //after a few iterations, update
{ //user interface
Synchronize(update); //fails: never returns from Synchronize call
update_count=0;
}
经过很多失败的尝试,我想出了这个。
//in the thread...
update_count++;
if(update_count>rate)
{
EnterCriticalSection(&Form1->mylock1);
Form1->tempimage->Assign(Imgcopy); //tempimage is another bitmap,
InterlockedExchange(&Form1->imageready,1);//declared in the main Form
LeaveCriticalSection(&Form1->mylock1); //and is only ever accessed
update_count=0; //inside a critical section
}
//...and in the main Form....
if(imageready==1)
{
EnterCriticalSection(&mylock1);
Image9->Picture->Bitmap->Assign(tempimage); //Fails here
InterlockedExchange(&gotimage,1);
InterlockedExchange(&imageready,0);
LeaveCriticalSection(&mylock1);
}
所以,无奈之下。
//in the thread...
update_count++;
if(update_count>rate)
{
Synchronize(update);
EnterCriticalSection(&Form1->mylock1);
Form1->tempimage->Assign(Imgcopy);
Clipboard()->Assign(Imgcopy);
InterlockedExchange(&Form1->imageready,1);
LeaveCriticalSection(&Form1->mylock1); */
update_count=0;
}
//and in the main Form...
if(imageready==1)
{
EnterCriticalSection(&mylock1);
if (Clipboard()->HasFormat(CF_BITMAP))
{
Image9->Picture->Bitmap->Assign(Clipboard());
}
InterlockedExchange(&gotimage,1);
InterlockedExchange(&imageready,0);
LeaveCriticalSection(&mylock1);
}
这最后的努力工作,虽然比较慢,因为剪贴板的开销,这是一个可怜的拐杖,在最好的。我怀疑剪贴板正在执行一个否则失败的同步工作,但正如我前面所说,我无法理解为什么。可能是什么问题?
从主UI线程的上下文之外访问VCL UI控件是不安全的。工作线程必须与主线程同步才能正确访问它们。关键部分不会那样做。但是,只要*每个线程锁定位图的'Canvas','TBitmap'对象本身就可以安全地跨越线程边界使用*。只是不要直接在工作线程中操作'TImage'的位图。使用内存中的'TBItmap'很好,然后在需要显示新的'TBitmap'内容时与主UI线程同步。如果遇到冻结问题,你正在做错事... –
您不需要使用'Canvas-> TryLock()',只需使用'Canvas-> Lock()'来代替。当你完成时,不要忘记调用'Canvas-> Unlock()'。请勿长时间握住锁。让工作线程等待,直到需要更改,然后获取锁定,进行更改,解锁和同步。当主UI线程检测到更改时,让它获得锁定,读取新位图并解锁。 –
我发现如果我不在创建时锁定线程本地图像,并将它们锁定直到删除,则线程完成访问NULL图像指针或空白图像。 Synchronize()是否只调用“postmessage”并返回,还是在返回之前等待主Form的某些响应?如果它是前者,那可能会解释Synchronize()调用失败 - 我们将再次处理图像副本,主要Form试图访问它(代码块1,上面),但即使是这种情况,关键部分版本的问题(代码块2和3)仍然存在。 –