2016-02-29 156 views
2

我想在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); 
} 

这最后的努力工作,虽然比较慢,因为剪贴板的开销,这是一个可怜的拐杖,在最好的。我怀疑剪贴板正在执行一个否则失败的同步工作,但正如我前面所说,我无法理解为什么。可能是什么问题?

+1

从主UI线程的上下文之外访问VCL UI控件是不安全的。工作线程必须与主线程同步才能正确访问它们。关键部分不会那样做。但是,只要*每个线程锁定位图的'Canvas','TBitmap'对象本身就可以安全地跨越线程边界使用*。只是不要直接在工作线程中操作'TImage'的位图。使用内存中的'TBItmap'很好,然后在需要显示新的'TBitmap'内容时与主UI线程同步。如果遇到冻结问题,你正在做错事... –

+0

您不需要使用'Canvas-> TryLock()',只需使用'Canvas-> Lock()'来代替。当你完成时,不要忘记调用'Canvas-> Unlock()'。请勿长时间握住锁。让工作线程等待,直到需要更改,然后获取锁定,进行更改,解锁和同步。当主UI线程检测到更改时,让它获得锁定,读取新位图并解锁。 –

+0

我发现如果我不在创建时锁定线程本地图像,并将它们锁定直到删除,则线程完成访问NULL图像指针或空白图像。 Synchronize()是否只调用“postmessage”并返回,还是在返回之前等待主Form的某些响应?如果它是前者,那可能会解释Synchronize()调用失败 - 我们将再次处理图像副本,主要Form试图访问它(代码块1,上面),但即使是这种情况,关键部分版本的问题(代码块2和3)仍然存在。 –

回答

0

感谢您的意见,雷米。他们在试图解决问题的同时,让我摆脱了自己陷入的“眩晕”。我忘记了Windows需要移动内存块,并且如果锁定它们,则无法执行此操作。

同步(更新)调用(上面的代码块1)的最初问题是由于我在调用过程中仍然有工作副本(Imgcopy)被锁定(来自线程内部),导致主窗体无法访问它。我怀疑(但尚未调查-代码已经消失)相同的根本原因在代码块2中工作。

在访问之前锁定每个位图,之后立即解锁已解决此问题。

Peter O,感谢您的编辑 - 我没有意识到在我的第一篇文章中有太多开销。