2016-02-29 154 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,感謝您的編輯 - 我沒有意識到在我的第一篇文章中有太多開銷。