通过线程下载图像错误

2024-01-06

祝所有 StackOverFlow 会员和读者新年快乐!

我今天来找你询问有关 Delphi 中线程的问题(我浏览了大部分已经发布的关于该主题的内容,但找不到线索)。

我有一个非常简单的测试应用程序,其中包含一个表单(frmIMGDown)和一个线程单元。 表格上可以找到

  • 一个T按钮
  • a TImage
  • 一个进度条

单击时,该按钮会启动一个线程,从网络下载图像,在此过程中更新进度条并在 Timage 中显示下载的图像。

只要调用表单 (frmIMGDown) 是主申请表单,就可以正常工作,或者如果从另一种形式调用它但是所有表格均在应用程序启动时创建.

现在,如果我通过按钮动态创建 frmIMGDown,请单击主窗体:

procedure TForm1.Button2Click(Sender: TObject);
var
  frmIMGDown : TfrmIMGDown;
begin
  try
    frmIMGDown := TfrmIMGDown.Create(nil);
    frmIMGDown.ShowModal;
  finally
    frmIMGDown.Free;
  end;
end;

我得到一个地址访问冲突... error

如果我改变

frmIMGDown := TfrmIMGDown.Create(nil);

to

frmIMGDown := TfrmIMGDown.Create(Form1);

结果是相同的,但有相同的错误。

我怀疑这与我实现的线程以及可能使用的变量有关,并且我尝试发送回 frmIMGDown,但我找不到解决方案。

这是线程单元:

unit unit_MyThread;

interface

uses
  Classes, IdHTTP, VCL.Forms, SyStem.UITypes, SysUtils, VCL.Dialogs, Graphics, IdTCPClient, IdTCPConnection, IdComponent,IdBaseComponent;

type
  TIdHTTPThread = class(TThread)
  private
    FURL : String;
    idHTTP: TIdHTTP;
    B : TBitMap;
    W : TWICImage;
    //MS : TMemoryStream;
  public
    Constructor Create(CreateSuspended: Boolean);
    Destructor Destroy; override;
    Property URL : String read FURL WRITE FURL;
    procedure OnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
    procedure OnWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
    procedure OnWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
  protected
    procedure Execute; override;
  end;

implementation
uses
  unit_IMG_Down;

Constructor TiDHTTPThread.Create(CreateSuspended: Boolean);
begin
  inherited Create(Suspended);
  IdHTTP := TIdHTTP.Create;
  Screen.Cursor := crHourGlass;
  IdHTTP.onWork := OnWork;
  IdHTTP.OnWorkbegin := OnWorkBegin;
  IdHTTP.OnWorkEnd := OnWorkEnd;
  B := TBitmap.Create;
  W := TWICImage.Create;
end;

Destructor TIdHTTPThread.Destroy;
begin
  idHTTP.Free;
  B.Free;
  W.Free;
  Screen.Cursor := crDefault;
  inherited Destroy;
end;

procedure TIdHTTPThread.Execute;
var
  MS : TMemoryStream;
begin
  Screen.Cursor := crHourGlass;
    try
      MS := TMemoryStream.Create;
      try
        IdHTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';

        IdHTTP.Get(URL,MS);
        MS.Position := 0;
        W.LoadFromStream(MS);
        B.Assign(W);
        frmIMGDown.Image3.Picture.Assign(B);
      except
        On E: Exception do ShowMessage(E.Message);
      end;
    finally
      MS.Free;
    end;
end;

procedure TIdHTTPThread.OnWork(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
var
  Http: TIdHTTP;
  ContentLength: Int64;
  Percent: Integer;
begin
  Http := TIdHTTP(ASender);
  ContentLength := Http.Response.ContentLength;

  if (Pos('chunked', LowerCase(Http.Response.TransferEncoding)) = 0) and
     (ContentLength > 0) then
  begin
    Percent := 100*AWorkCount div ContentLength;
    frmIMGDown.ProgressBar3.Position := AWorkCount +2;
    frmIMGDown.ProgressBar3.Position := AWorkCount -1;
  end;
end;

procedure TIdHTTPThread.OnWorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Int64);
begin
  frmIMGDown.ProgressBar3.Visible := True;
  frmIMGDown.ProgressBar3.Position := 0;
end;

procedure TIdHTTPThread.OnWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  frmIMGDown.ProgressBar3.Visible := false;
end;

end.

以及从按钮调用线程

procedure TfrmIMGDown.Button3Click(Sender: TObject);
var
  HTTPThread : TIdHTTPThread;
begin
  HTTPThread := TIdHTTPThread.Create(False);
  HTTPThread.URL := 'https://bw-1651cf0d2f737d7adeab84d339dbabd3-bcs.s3.amazonaws.com/products/product_119522/Full119522_283b3acc91f119ab4b2939b1beb67211.jpg';

  HTTPThread.FreeOnTerminate := True;
end;

旁注:我使用 TWICImage 下载图像(LoadFromStream),因为我不知道图像将采用哪种格式(这里 URl 是硬编码用于测试),然后将其分配给 TBitmap。

提前致谢,再次祝大家新年快乐。

Math


您的线程正在访问表单的全局指针变量。当您收到访问冲突错误时,这是​​因为您没有将新的 Form 对象分配给该全局变量,而是将其分配给同名的局部变量。所以当线程试图访问全局指针时它是无效的。

解决方案是让 Form 对象传递它的Self指向线程的指针,然后将其存储在线程的成员中。根本不要依赖全局指针。

更好的解决方案是根本不让线程知道有关 UI 的任何信息。我建议在线程类中定义事件,并让线程在需要时触发这些事件(图像下载、进度更新、错误等)。然后,表单可以为这些事件分配处理程序,以根据需要更新 UI。

此外,在访问表单的 UI 控件时,您的线程未与主线程同步。 VCL 不是线程安全的,因此您必须同步对 UI 的访问。甚至TBitmap不是线程安全的(不确定TWICImage), 你必须Lock its Canvas在线程中使用它时,并且Unlock完成后。

此外,您还存在竞争条件,因为您允许线程(可能)在分配其线程之前开始运行URL and FreeOnTerminated价值观。您需要创建处于挂起状态的线程,并且在完成初始化之后才开始运行它。最好的方法是创建线程CreateSuspended=False并处理线程构造函数本身中的所有初始化。线程在其构造函数退出之前不会开始运行。否则,创建线程CreateSuspended=True并在准备好时显式恢复它。

话虽如此,尝试更多类似这样的事情:

unit unit_MyThread;

interface

uses
  Classes, IdComponent, IdBaseComponent;

type
  THTTPStage = (HTTPInit, HTTPDownloading, HTTPDone);
  THTTPStatusEvent = procedure(Sender: TObject; Progress, Total: Int64; Stage: THTTPStage) of object;
  THTTPImageEvent = procedure(Sender: TObject; Data: TStream) of object;

  THTTPThread = class(TThread)
  private
    FURL : String;
    FStream : TMemoryStream;
    FProgress, FTotal : Int64;
    FStage : THTTPStage;
    FOnStatus : THTTPStatusEvent;
    FOnImage : THTTPImageEvent;
    procedure DoOnStatus;
    procedure DoOnImage;
    procedure HTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
    procedure HTTPWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
    procedure HTTPWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
  protected
    procedure Execute; override;
  public
    constructor Create(const AURL: string);
    property OnStatus: THTTPStatusEvent read FOnStatus write FOnStatus;
    property OnImage: THTTPImageEvent read FOnImage write FOnImage;
  end;

implementation

uses
  IdTCPClient, IdTCPConnection, IdHTTP;

constructor THTTPThread.Create(const AURL: string);
begin
  inherited Create(True);
  FreeOnTerminate := True;
  FURL := AURL;
end;

procedure THTTPThread.Execute;
var
  IdHTTP: TIdHTTP;
begin
  IdHTTP := TIdHTTP.Create;
  try
    IdHTTP.OnWork := HTTPWork;
    IdHTTP.OnWorkBegin := HTTPWorkBegin;
    IdHTTP.OnWorkEnd := HTTPWorkEnd;
    IdHTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
    FStream := TMemoryStream.Create;
    try
      IdHTTP.Get(FURL, FStream);
      FStream.Position := 0;
      if Assigned(FOnImage) then
        Synchronize(DoOnImage);
    finally
      FStream.Free;
    end;
  finally
    IdHTTP.Free;
  end;
end;

procedure THTTPThread.DoOnStatus;
begin
  if Assigned(FOnStatus) then
    FOnStatus(Self, FProgress, FTotal, FStage);
end;

procedure THTTPThread.DoOnImage;
begin
  if Assigned(FOnImage) then
    FOnImage(Self, FStream);
end;

procedure THTTPThread.HTTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
  if AWorkMode = wmRead then
  begin
    FProgress := AWorkCount;
    FStage := HTTPDownloading;
    if Assigned(FOnStatus) then
      Synchronize(DoOnStatus);
  end;
end;

procedure THTTPThread.HTTPWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
  if AWorkMode = wmRead then
  begin
    FProgress := 0;
    FTotal := AWorkCountMax;
    FStage := HTTPInit;
    if Assigned(FOnStatus) then
      Synchronize(DoOnStatus);
  end;  
end;

procedure THTTPThread.HTTPWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  if AWorkMode = wmRead then
  begin
    FProgress := FTotal;
    FStage := HTTPDone;
    if Assigned(FOnStatus) then
      Synchronize(DoOnStatus);
  end;
end;

end.
procedure TfrmIMGDown.Button3Click(Sender: TObject);
var
  HTTPThread : THTTPThread;
begin
  HTTPThread := THTTPThread.Create('https://bw-1651cf0d2f737d7adeab84d339dbabd3-bcs.s3.amazonaws.com/products/product_119522/Full119522_283b3acc91f119ab4b2939b1beb67211.jpg');
  HTTPThread.OnStatus := HTTPStatus;
  HTTPThread.OnImage := HTTPImage;
  HTTPThread.OnTerminate := HTTPTerminated;
  HTTPThread.Resume;
end;

procedure TfrmIMGDown.HTTPStatus(Sender: TObject; Progress, Total: Int64; Stage: THTTPStage);
begin
  case Stage of
    HTTPInit: begin
      ProgressBar3.Visible := True;
      ProgressBar3.Position := 0;
      ProgressBar3.Max := 100;
      Screen.Cursor := crHourGlass;
    end;
    HTTPDownloading: begin
      if Total <> 0 then
        ProgressBar3.Position := 100*Progress div Total;
    end;
    HTTPDone: begin
      ProgressBar3.Visible := false;
      Screen.Cursor := crDefault;
    end;
end;

procedure TfrmIMGDown.HTTPImage(Sender: TObject; Data: TStream);
var
  J: TJPEGImage;
begin
  J := TJPEGImage.Create;
  try
    J.LoadFromStream(Data);
    Image3.Picture.Assign(J);
  finally
    J.Free;
  end;
end;

procedure TfrmIMGDown.HTTPTerminated(Sender: TObject);
begin
  if TThread(Sender).FatalException <> nil then
    ShowMessage(Exception(TThread(Sender).FatalException).Message);
end;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过线程下载图像错误 的相关文章

  • numba 函数何时编译?

    我正在研究这个例子 http numba pydata org numba doc 0 15 1 examples html multi threading http numba pydata org numba doc 0 15 1 ex
  • 在java中实现你自己的阻塞队列

    我知道这个问题之前已经被问过并回答过很多次了 但我只是无法根据互联网上找到的示例找出窍门 例如this http tutorials jenkov com java concurrency blocking queues html or t
  • 对象锁定私有类成员 - 最佳实践? (爪哇)

    I asked 类似的问题 https stackoverflow com questions 10548066 multiple object locks in java前几天 但对回复不满意 主要是因为我提供的代码存在一些人们关注的问题
  • Azure 事件中心 - 按顺序接收事件

    我使用下面的代码从 Azure Event Hub 接收事件 https learn microsoft com en us azure event hubs event hubs dotnet framework getstarted s
  • 报告线程进度的最佳方式

    我有一个程序 它使用线程顺序执行耗时的进程 我希望能够监视每个线程的进度 类似于BackgroundWorker ReportProgress ProgressChanged模型确实如此 我不能使用ThreadPool or Backgro
  • HttpSession 内的同步是否可行?

    UPDATE 问题后立即解决 问题 通常 同步是在 JVM 内序列化并行请求 例如 private static final Object LOCK new Object public void doSomething synchroniz
  • 在 .NET 并发线程之间传递数据的最佳方式是什么?

    我有两个线程 一个需要轮询一堆单独的静态资源以查找更新 另一种需要获取数据并将其存储在数据库中 线程1如何告诉线程2有东西要处理 如果数据块是独立的 则将数据块视为要由线程池处理的工作项 使用线程池和QueueUserWorkItem将数据
  • MainFormOnTaskbar + 工具提示导致焦点窃取

    我使用 Delphi XE2 构建了下面的代码 它创建 Form1 Form1 立即创建 Form2 的实例 当我按下 Form2 上的按钮时 会创建第二个 Form2 现在 如果我将鼠标悬停在第二个 最上面的 Form2 上的按钮上 并等
  • 使用 RxJava 限制吞吐量

    我现在遇到的情况很难解释 所以我会写一个更简单的版本来解释这个问题 我有一个Observable from 它发出一系列由ArrayList文件数量 所有这些文件都应上传到服务器 为此 我有一个函数可以完成这项工作并返回一个Observab
  • iOS AFNetworking downloadProgressBlock 计算返回负数

    我正在使用 AFNetworking 下载使用第三方类解析的数据 我之前曾多次使用 AFNetworking 执行类似的操作 但由于某种原因 当我调用 downloadProgressBlock 并进行计算以与我的进度条一起使用时 数字返回
  • 进度状态报告模式

    我正在实现需要显示进程栏 或进度百分比 的长时间运行的进程 长时间运行的过程的整体逻辑很复杂 各种分页数据检索 因此 我最终在代码中的不同位置硬编码了大量百分比 在更新完成百分比时 什么被认为是最佳设计模式 我发现 JFace 周围使用的模
  • 线程上下文类加载器和普通类加载器的区别

    线程的上下文类加载器和普通类加载器有什么区别 也就是说 如果Thread currentThread getContextClassLoader and getClass getClassLoader 返回不同的类加载器对象 将使用哪一个
  • 匹配集合 Parallel.Foreach

    我正在尝试为 matchcollection 创建一个 Parallel Foreach 循环 它在我构建的刮刀中 我只需要知道在 Parallel Foreach 中放入什么 MatchCollection m Regex Matches
  • FireDac 添加下划线 1 以区分具有相同名称的 2 个列名

    我有一个连接 2 个表的选择 因此这些表中存在具有相似名称的列 因此现在在检索结果时 FireDac 将下划线 1 添加到第二个列名称以区分这两个表 Select from Table1 inner join Table2 on Table
  • 在生产者-消费者情况下使用条件变量

    我正在尝试了解条件变量以及如何在生产者 消费者情况下使用它 我有一个队列 其中一个线程将数字推入队列 而另一个线程从队列中弹出数字 当生产线程放置一些数据时 我想使用条件变量向消费线程发出信号 问题是有时 或大多数时候 它只将最多两个项目推
  • TensorFlow 2.0:在自定义训练循环中显示进度条

    我正在为音频分类任务训练 CNN 并且使用带有自定义训练循环的 TensorFlow 2 0 RC 如中所述本指南 https www tensorflow org beta guide keras training and evaluat
  • Julia Threads.@threads 比单线程性能慢

    我正在尝试求解一维热方程的数值 我正在使用有限差分 并且在 Julia 中使用 threads 指令时遇到一些问题 特别是下面有相同代码的两个版本 第一个是单线程 而另一个使用 threads 除了 thread指令之外 它们是相同的 fu
  • Play 框架:异步与同步性能

    我有以下代码 def sync Action val t0 System nanoTime Thread sleep 100 val t1 System nanoTime Ok Elapsed time t1 t0 1000000 0 ms
  • 在 Delphi 中将对象转换为 OleVariant

    有没有办法在 OleVariant 中传递包装并解开 TObject 后代 我正在尝试跨自动化对象传递 TObject 我知道这不是一个好主意 但我没有更好的选择 该对象将在来自同一自动化 dll 的对象之间传递 如果这有什么区别的话 像这
  • GCD 与自定义队列

    我想知道这两者的性能有什么区别 dispatch async dispatch get global queue DISPATCH QUEUE PRIORITY HIGH 0 perform complex operation dispat

随机推荐