我想在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)仍然存在。 –