线程执行后Progress Bar为NULL

Progress Bar is NULL after the execution of a thread

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

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

线程构造函数:

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

以及进度条的更新方法:

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

    TabbedwithNavigationForm->ProgressBar->Value = sent;
}

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

话虽如此,您的帖子组织得不是很好。还有一种更简单的方法来处理分块——让 Indy 为您完成。它有一个 OnWork 事件,您可以将其用于 ProgressBar 更新。

试试像这样的东西:

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