多线程同步 我试图用Borland的C++ Builder 6在WiXXP SP3上编写一个多线程的图形操作程序,但是我遇到了一个同步问题,并且不能理解为什么。

多线程同步 我试图用Borland的C++ Builder 6在WiXXP SP3上编写一个多线程的图形操作程序,但是我遇到了一个同步问题,并且不能理解为什么。,c++,multithreading,graphics,c++builder,C++,Multithreading,Graphics,C++builder,主窗体(Form1)有一个从文件加载的TPicture。线程通过一个Synchronize()调用获取该文件的副本,并且可以正常工作。线程对图像进行一些处理,理论上,它会定期更新主窗体图像。主窗体还控制一台机器,是“首选”紧急停止,因此阻塞不是选项。在主窗体获得工作副本或工作副本的副本(抱歉,但必须如此)之前,一切都正常,此时程序挂起,并且只响应IDE的“程序重置”。一个糟糕的解决方案是将工作图像复制到剪贴板,然后从主窗体将其从剪贴板复制到主窗体的图像 //Synchroniza

主窗体(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);
}

由于剪贴板的开销,最后一次尝试虽然相对缓慢,但效果很好,充其量也不是什么好办法。我怀疑剪贴板正在强制执行一个失败的同步工作,但是,正如我前面所说的,我不明白为什么。问题是什么

谢谢你的评论,雷米。他们让我摆脱了在试图解决问题时陷入的“紧张”。我忘了Windows需要移动内存块,如果锁定了内存块,就不能这样做

Synchronize(update)调用(上面的代码块1)的初始问题是由于我在调用过程中仍将工作副本(Imgcopy)锁定(从线程内部),从而阻止主窗体随后访问它。我怀疑(但尚未调查-代码已经消失)代码块2中的根本原因是相同的

在访问之前锁定每个位图,然后立即解锁解决了这个问题


Peter O,谢谢你的编辑-我不知道我最初的帖子有这么多开销

从主UI线程的上下文之外访问VCL UI控件是不安全的。工作线程必须与主线程同步才能正确访问它们。关键部分不会这样做。但是,
TBitmap
对象本身可以安全地跨线程边界使用,只要每个线程都锁定位图的
画布
。只是不要直接在工作线程中操作
TImage
的位图。使用内存中的
TBItmap
可以,然后在需要显示新的
TBItmap
内容时与主UI线程同步。如果遇到冻结,说明您做错了……您不需要使用
Canvas->TryLock()
,只需使用
Canvas->Lock()
。完成后不要忘记调用
Canvas->Unlock()
。不要长时间握住锁。让工作线程等待需要的更改,然后获取锁、进行更改、解锁和同步。当主UI线程检测到更改时,让它获得锁,读取新位图,然后解锁。我发现,如果在创建时不锁定线程本地图像,并将其锁定直到删除,线程将完成访问空图像指针或空白图像。Synchronize()是否只调用“postmessage”并返回,还是在返回前等待主窗体的响应?如果是前者,这可能会解释Synchronize()调用失败的原因-我们将再次处理映像副本,而主窗体正在尝试访问它(上面的代码块1),但即使是这样,关键部分版本的问题(代码块2和3)仍然存在。请不要在线程生存期内锁定位图画布。而是根据需要锁定和解锁它。对于
TThread.Synchronize()
,它是同步的。它将请求发布到主线程,并且在处理请求之前不会退出<另一方面,code>TThread.Queue()是异步的。它不会等待处理请求。