【问题标题】:Progress Bar is NULL after the execution of a thread线程执行后进度条为 NULL
【发布时间】:2016-01-12 16:41:55
【问题描述】:

我有一个带有进度条的表单和一个将 xml 上传到服务器的按钮。 当按下按钮时,会创建一个新线程来创建一个套接字,然后它将数据以块的形式发送到服务器,同时它会更新进度条。 现在,当第二次按下上传按钮时,我遇到了访问冲突,并且在调试器中,进度条对象的地址为 NULL。 我不知道为什么进度条会被释放,所以如果有人有任何想法,我将不胜感激。

附:目标操作系统是windows P.S.2如果相同的代码在没有使用线程的情况下在主线程上运行,那么我似乎没有这个问题,即使我在线程中整体跳过进度条的使用,它也会在之后再次设置为 null第一次按下上传按钮。

线程构造函数:

__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;
}

执行方法:

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

两个补充功能:

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;
    }
}

还有进度条的Update方法:

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

    TabbedwithNavigationForm->ProgressBar->Value = sent;
}

【问题讨论】:

    标签: c++builder firemonkey c++builder-xe8


    【解决方案1】:

    我在这段代码中看不到任何会导致 ProgressBar 指针变为NULL 的内容。因此,要么是您破坏了内存,要么是此处未显示的其他代码中的其他内容是罪魁祸首。无论哪种方式,要解决此问题,您可以在第一次运行线程之前在 IDE 调试器中运行您的应用程序并在 ProgressBar 变量上设置 Data Breakpoint。如果有东西改变了那个指针的值,断点就会被命中,你可以查看调用堆栈来弄清楚发生了什么。

    话虽如此,您的帖子组织得不是很好。还有一种更简单的方法来处理分块 - 让 Indy 为您完成。它有一个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;
    }
    

    【讨论】:

    • 好的,我会用调试器试试,非常感谢“Worker”替代方案。我想我会走这条路,因为它看起来比我用来管理块的方式更清晰和易于理解(而且我认为我的块代码更容易出错:p)
    • 我发现了问题所在...我有一个地图组件和一个用于启用或禁用 gps 组件的按钮。当 gps 激活 gps 时,它还会使用以下代码清理地图以删除标记(或者至少我认为我正在这样做): //for(int i = 0; i ChildrenCount; i++) // MapView->Children->ToArray()[i]->Release();问题是它释放了一些对象而不是标记
    猜你喜欢
    • 2017-08-08
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    • 2016-06-24
    • 2011-07-28
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    相关资源
    最近更新 更多