- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
祝所有 StackOverFlow 成员(member)和读者新年快乐!
我今天来找你是为了询问有关 Delphi 中线程的问题(我浏览了大部分已经发布的关于该主题的内容,但找不到线索)。
我有一个非常简单的测试应用程序,其中包含一个表单(frmIMGDown)和一个线程单元。在表格上找到
单击时,该按钮会启动一个线程,从网络下载图像,在此过程中更新进度条并在 Timage 中显示下载的图像。
只要调用表单 (frmIMGDown) 是主应用程序表单,或者如果从另一个表单调用它,但所有表单都是在应用程序启动时创建,则此操作正常>.
现在,如果我通过按钮动态创建 frmIMGDown,请单击主窗体:
procedure TForm1.Button2Click(Sender: TObject);
var
frmIMGDown : TfrmIMGDown;
begin
try
frmIMGDown := TfrmIMGDown.Create(nil);
frmIMGDown.ShowModal;
finally
frmIMGDown.Free;
end;
end;
我收到地址访问冲突...错误
如果我改变
frmIMGDown := TfrmIMGDown.Create(nil);
至
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。
提前致谢,再次祝大家新年快乐。
数学
最佳答案
您的线程正在访问表单的全局指针变量。当您收到访问冲突错误时,这是因为您没有将新的 Form 对象分配给该全局变量,而是将其分配给同名的局部变量。所以当线程试图访问全局指针时它是无效的。
解决方案是让 Form 对象将其 Self
指针传递给线程,然后将其存储在线程的成员中。根本不要依赖全局指针。
更好的解决方案是根本不让线程知道有关 UI 的任何信息。我建议在线程类中定义事件,并让线程在需要时触发这些事件(图像下载、进度更新、错误等)。然后,表单可以为这些事件分配处理程序,以根据需要更新 UI。
此外,在访问表单的 UI 控件时,您的线程未与主线程同步。 VCL 不是线程安全的,因此您必须同步对 UI 的访问。即使 TBitmap
也不是线程安全的(不确定 TWICImage
),您在使用时必须Lock
其Canvas
它在一个线程中,完成后解锁
。
此外,您还存在竞争条件,因为您允许线程(可能)在分配其 URL
和 FreeOnTermulated
值之前开始运行。您需要创建处于挂起状态的线程,并且在完成初始化之后才开始运行它。最好的方法是使用 CreateSuspished=False 创建线程,并在线程的构造函数本身中处理所有初始化。线程在其构造函数退出之前不会开始运行。否则,使用 CreateSuspished=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;
关于multithreading - 通过线程下载图像错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48145144/
我有2个功能: function func1() while true do -- listen on connection end end function func2()
我的问题可能看起来很奇怪,但我想我正面临着 volatile 的问题。对象。 我写了一个这样实现的库(只是一个方案,不是真正的内容): (def var1 (volatile! nil)) (def
由于 maven 支持多线程构建,是否可以同时运行 Sonar 多线程? (例如 mvn sonar:sonar -T 4 ) 我运行了它,当模块报告成功时,它报告整个构建失败并返回 java.uti
我们正在启动一个网站,该网站在短时间内的交易量非常大。它基本上是在给票。该代码是用Java,Spring和Hibernate编写的。我想通过产生多个线程并尝试使用JUnit测试用例来获取票证来模仿高容
我正在尝试访问像素数据并将图像从游戏中的相机保存到磁盘。最初,简单的方法是使用渲染目标,然后使用RenderTarget-> ReadPixels(),但是由于ReadPixels()的 native
我们有以下系统: 用户数:〜500k 项目数:〜100k UserSimilarity userSimilarity = new TanimotoCoefficientSimilarity(dataM
也许这是一个经常出现的问题,但我需要根据我的上下文进行一些自定义。 我正在使用 Spring Batch 3.0.1.RELEASE 我有一个简单的工作,有一些步骤。一个步骤是这样的 block :
也许这是一个经常出现的问题,但我需要根据我的上下文进行一些自定义。 我正在使用 Spring Batch 3.0.1.RELEASE 我有一个简单的工作,有一些步骤。一个步骤是这样的 block :
我正在尝试使用PyBrain和Python的multiprocessing软件包在Python中训练神经网络。 这是我的代码(它训练了一个简单的神经网络来学习XOR逻辑)。 import pybrai
我有一个繁重的功能,不适合在主时间轴上执行(因为要花很长时间才能完成并使程序崩溃)。 因此我在air(as3)中搜索多线程,但是我发现的所有示例都说明了如何在worker中运行单独的swf文件。如何在
我想实现线程A 和线程B 并行运行并共享全局变量。 下面是用python编写的代码。我想在中执行相同操作Dart (我不想使用future等待,因为它正在等待其他线程完成或必须等待。) 大小写变量:
我的一个项目只适用于调试 DLL,而不适用于非调试 DLL。 在 Debug DLL 设置下发布项目有哪些注意事项?例如,是否丢失了某些优化? 如何通过将调试版本设置为非调试 DLL 来调试此项目?我
我正在尝试比较 Matlab 和 Julia 之间的速度和性能。我正在查看一个代码,该代码对承受给定负载的连续体结构进行拓扑优化。我正在查看的代码是公共(public)代码topopt88.m:htt
Serving Flask 应用程序“服务器”(延迟加载) 环境:生产警告:这是一个开发服务器。不要在生产部署中使用它。请改用生产 WSGI 服务器。 Debug模式:开启 在 http://0.0.
我对 PyQT 很陌生。我正在学习如何制作 Progressbar 并随着算法的进展对其进行更新。我已经能够制作一个使用此链接进行 self 更新的基本进度条:Python pyqt pulsing
我正在尝试指定在特定线程上运行任务,这样我就可以使用两个专用于“放入” channel 的耗时任务的线程,而其他线程则用于处理该任务。 我对如何将特定任务分配给特定线程感到困惑。我以为我可以使用类似
我正在编写一个软件,它对很多(潜在的大)图像进行大量图像操作/合成。 多线程有助于提高速度,但 QT 不允许同时在同一图像上使用多个 QPainter。 所以我必须在副本的每个线程中进行图像操作/合成
此脚本读取 url 文件以执行多线程 HTTP 请求。 如何使用带有 url 的数组来发出多线程请求? 我的阵列将有类似的东西: @array = ("https://example.com/xsd"
Java 文档声明了以下关于构造函数同步的内容: Note that constructors cannot be synchronized — using the synchronized keyw
我有一个程序,其中主线程创建了很多线程。它崩溃了,我正在调试核心文件。崩溃发生在其中一个子线程中。为了找到原因,我需要知道主线程是否还活着。有什么方法可以找出哪个线程是初始线程? 最佳答案 Is th
我是一名优秀的程序员,十分优秀!