2016-01-12 98 views
0

我有一個帶有進度條和上傳xml到服務器的按鈕的表單。 按下按鈕時會創建一個新線程,該線程將創建一個套接字,然後以塊的形式將數據發送到服務器,同時它會更新進度條。 現在,當第二次按上傳按鈕時,我得到訪問衝突,並且在調試器中進度欄對象的地址爲NULL。 我不明白爲什麼進度條被釋放,所以如果任何人有任何想法,我會很感激。線程執行後進度條爲空

P.S.目標操作系統是windows PS2如果相同的代碼在主線程上運行而沒有使用線程,那麼我似乎沒有這個問題,即使我在線程中總體上忽略了進度條的使用在第一次上傳按鈕後再次設置爲空。

螺紋構造:

__fastcall UploadRouteThread::UploadRouteThread(bool CreateSuspended) : TThread(CreateSuspended) 
{ 
    this->OnTerminate = OnTerminateHandler; 
    ioHandlerStack = new TIdIOHandlerStack(); 
    tcpClient = new TIdTCPClient(); 

    tcpClient->ReadTimeout = -1; 
    tcpClient->UseNagle = true; 

    tcpClient->IOHandler = ioHandlerStack; 
    tcpClient->OnConnected = OnConnectedHandler; 

} 

的OnTerminate處理程序:

void __fastcall UploadRouteThread::OnTerminateHandler(TObject *Sender) 
{ 
    TabbedwithNavigationForm->UploadButton->Text = "Upload"; 
    TabbedwithNavigationForm->UploadButton->Enabled = false; 
    TabbedwithNavigationForm->ProgressBar->Visible = false; 

    tcpClient->DisconnectNotifyPeer(); 

    ShowMessage("Data uploaded."); 

    delete ioHandlerStack; 
    delete tcpClient; 

    TabbedwithNavigationForm->OptionButton->Enabled = true; 
    TabbedwithNavigationForm->RetrieveRoutesButton->Enabled = true; 
    TabbedwithNavigationForm->TrackButton->Enabled = true; 
    TabbedwithNavigationForm->MediaButton->Enabled = true; 
} 

Execute方法:

void __fastcall UploadRouteThread::Execute() 
{ 
    FreeOnTerminate = true; 
    tcpClient->Connect(); 
} 

兩個supplumentary功能:

void __fastcall UploadRouteThread::SetHostPort(UnicodeString host, unsigned short port) 
{ 
    tcpClient->Host = host; 
    tcpClient->Port = port; 
} 

void __fastcall UploadRouteThread::SetXML(AnsiString xmlString) 
{ 
    this->xmlString = xmlString; 
} 

onConnect處理:

void __fastcall UploadRouteThread::OnConnectedHandler(TObject *Sender) 
{ 
    NextPacketSize nps; 
    TIdBytes bytes; 
    int chunks; 
    int bytesLength; 

    nps.PacketID = BasicPacket::DATA_UPLOAD; 
    nps.size = xmlString.Length(); 

    tcpClient->IOHandler->WriteDirect(RawToBytes(&nps, sizeof(nps)), sizeof(NextPacketSize)); 
    bytes = RawToBytes(xmlString.c_str(), xmlString.Length()); 

    bytesLength = bytes.get_length(); 
    chunks = ceil(float(bytesLength)/256.0); 

    int previousSizeSent(0); 
    for(int i = 1; i <= chunks; i++) 
    { 
     if(Terminated) 
      break; 
     int bytesToSend = 256; 
     TByteDynArray byteDynArray; 

     if((bytesToSend > bytesLength)) 
     { 
       bytesToSend = bytesLength; 
     } 

     byteDynArray = bytes.CopyRange(previousSizeSent, bytesToSend); 

     tcpClient->IOHandler->WriteDirect(ToBytes(byteDynArray, byteDynArray.get_length(), 0), 
     byteDynArray.get_length()); 

     sent = (float(i)/float(chunks)) * 100; 
     TThread::Synchronize(this, UpdateProgressBarInternal); 

     previousSizeSent += bytesToSend; 
     bytesLength -= bytesToSend; 
    } 
} 

和進度條的更新方法:

void __fastcall UploadRouteThread::UpdateProgressBarInternal() 
{ 
    if(!TabbedwithNavigationForm->ProgressBar->Visible) 
    { 
     TabbedwithNavigationForm->ProgressBar->Visible = true; 
     TabbedwithNavigationForm->ProgressBar->Max = 100; 
    } 

    TabbedwithNavigationForm->ProgressBar->Value = sent; 
} 

回答

1

我沒有看到這個代碼,任何會導致進度指針變成NULL。所以要麼是破壞內存,要麼是其他代碼中沒有顯示的其他代碼是罪魁禍首。無論哪種方式,爲了排除此問題,您可以在IDE調試器中運行您的應用程序,並在第一次運行線程之前在ProgressBar變量上設置一個數據斷點。如果有東西改變了該指針的值,斷點將會被觸發,你可以看看調用堆棧來判斷髮生了什麼。

這樣說,你的線程組織得不是很好。還有一種更簡單的方法來處理分塊 - 讓Indy爲你做。它有一個可用於ProgressBar更新的OnWork事件。

嘗試更多的東西是這樣的:

__fastcall UploadRouteThread::UploadRouteThread(String host, TIdPort port, AnsiString xmlString) 
    : TThread(false) 
{ 
    this->FreeOnTerminate = true; 
    this->OnTerminate = OnTerminateHandler; 
    this->xmlString = xmlString; 

    tcpClient = new TIdTCPClient(); 
    tcpClient->Host = host; 
    tcpClient->Port = port; 
    tcpClient->UseNagle = true; 
    tcpClient->OnWork = OnWorkHandler; 
} 

__fastcall UploadRouteThread::~UploadRouteThread() 
{ 
    delete tcpClient; 
} 

void __fastcall UploadRouteThread::OnTerminateHandler(TObject *Sender) 
{ 
    TabbedwithNavigationForm->UploadButton->Text = "Upload"; 
    TabbedwithNavigationForm->UploadButton->Enabled = false; 
    TabbedwithNavigationForm->ProgressBar->Visible = false; 

    if (FatalException) 
     ShowMessage("Data not uploaded."); 
    else 
     ShowMessage("Data uploaded."); 

    TabbedwithNavigationForm->OptionButton->Enabled = true; 
    TabbedwithNavigationForm->RetrieveRoutesButton->Enabled = true; 
    TabbedwithNavigationForm->TrackButton->Enabled = true; 
    TabbedwithNavigationForm->MediaButton->Enabled = true; 
} 

void __fastcall UploadRouteThread::OnWorkHandler(TObject *ASender, TWorkMode AWorkMode, __int64 AWorkCount) 
{ 
    if (Terminated) 
     Sysutils::Abort(); 

    sent = (double(AWorkCount) * 100.0)/xmlString.Length(); 

    // consider using TThread::Queue() instead so that you don't block 
    // the upload waiting for the UI to be updated... 
    TThread::Synchronize(this, &UpdateProgressBarInternal); 
} 

void __fastcall UploadRouteThread::Execute() 
{ 
    tcpClient->Connect(); 
    try 
    { 
     NextPacketSize nps; 
     nps.PacketID = BasicPacket::DATA_UPLOAD; 
     nps.size = xmlString.Length(); 
     tcpClient->IOHandler->Write(RawToBytes(&nps, sizeof(nps))); 

     tcpClient->BeginWork(wmWrite, xmlString.Length()); 
     tcpClient->IOHandler->Write(RawToBytes(xmlString.c_str(), xmlString.Length())); 
     tcpClient->EndWork(wmWrite); 

     /* alternatively: 
     TIdMemoryBufferStream *strm = new TIdMemoryBufferStream(xmlString.c_str(), xmlString.Length()); 
     try 
     { 
      // optional 
      tcpClient->IOHandler->SendBufferSize = 256; 

      // this calls (Begin|End)Work() internally... 
      tcpClient->IOHandler->Write(strm, 0, false); 
     } 
     __finally 
     { 
      delete strm; 
     } 
     */ 
    } 
    __finally 
    { 
     tcpClient->Disconnect(); 
    } 
} 

void __fastcall UploadRouteThread::UpdateProgressBarInternal() 
{ 
    if (!TabbedwithNavigationForm->ProgressBar->Visible) 
    { 
     TabbedwithNavigationForm->ProgressBar->Visible = true; 
     TabbedwithNavigationForm->ProgressBar->Max = 100; 
    } 

    TabbedwithNavigationForm->ProgressBar->Value = sent; 
} 
+0

好吧,我會嘗試與調試,並感謝了很多的「工人」替代。我想我會這樣做,因爲它看起來比我用來管理塊的方式更清潔和易於理解(並且我認爲我的塊代碼更容易出錯:p) –

+0

我發現了什麼是錯的...我有一個地圖組件和一個啓用或禁用GPS組件的按鈕。當gps激活gps時,它還通過使用以下代碼清除地圖以刪除標記(或者至少這是我認爲我正在做的)標記:\t \t // for(int i = 0; i < MapView-> ChildrenCount; i ++ ) \t \t // \t MapView-> Children-> ToArray()[i] - > Release();事情是它釋放了一些對象而不是標記 –