gpt4 book ai didi

multithreading - 图像浏览程序 - 为什么它在线程化后会随机崩溃?

转载 作者:行者123 更新时间:2023-12-03 15:10:46 26 4
gpt4 key购买 nike

长话短说,我离熟练的程序员还很远,事实上,到目前为止,我最复杂的程序要么是简单的 ASCII 字符串操作、简单的数学运算和数组搜索/排序,要么是 Free Pascal 或更高版本、Delphi 7 和 Java。那是几年前,当我在高中教员学习编程时(那是普通的 Pascal)。后来我成为了一名程序员(遇到了D7和Java,还有一些C++),但由于个人原因我退出了我的编程学习,从那时起,我没有写过一行代码。

嗯,抱歉介绍的太长了,所以... 最近决定重温编程作为自己的爱好,主要是因为有些长期以来一直想完成的任务没有找到合适的程序。尽管我对显式参数、指针、对象、类、构造函数和线程等相当基本的东西的理解很模糊,但在编程书籍、Delphi 帮助文件和互联网的帮助下,我还是设法在 Delphi 7 中编写了一个简单的程序可以使用外部库在给定目录中加载和显示某些图像文件格式,可以在它们之间任意切换(使用 GUI),并在文本文件中记录一些信息(主要用于调试目的)。

但是,我在当前版本的代码中尝试使图像加载和显示功能线程化时遇到了问题。首先,为了更好地理解,我将解释我的程序的逻辑。

首先,在主窗体的FormCreate事件中,程序会在当前目录(exe所在的目录)中寻找支持的图片文件。如果没有图像文件,则将当前目录设置在上一级(带有标准的 Windows 文件系统符号“..”)并检查图像。支持的图像文件由文件扩展名决定。无论如何,这个资源管理器函数将找到的图像的文件名和文件类型标识符(它是一个字节)存储在一个动态数组中。然后,使用这个数组作为引用,第一个支持的图像文件被正确的库加载并显示在表单中。 GUI 有按钮和组合框可以在图像之间切换,每个控件的 OnClick 或 OnSelect(组合框)事件设置有关假定当前图像文件的变量,并调用使用引用数组的图像加载器和显示器函数。

问题是有些图像太大以至于加载需要很长时间,因此在图像完全加载和显示之前 GUI 无法响应。我试图通过将每个图像加载器函数初始化为一个线程来使这个程序成为线程。虽然 GUI 现在 react 更快,但该程序肯定有两个新错误。

第一个是程序在更改图像时有时会随机崩溃,出现的消息要么是指“ JPEG Error #58 ”(据说是 Delphi 内置 jpeg 库中的“无效文件结构”),“”204Access 异常“ EOSError”异常(包括“系统错误,代码5 ‘),’未知的软件异常”,“运行时错误216 ”,以及有关内存位置错误消息和失败读取操作。在使用线程之前,这些错误消息都没有出现,但我当然想(并且必须)在程序中使用线程。

另一个小错误是,当快速连续单击界面按钮时,似乎所有加载和显示都发生了,尽管以滞后然后快速的方式进行。我真的不知道如何“杀死”一个线程并“再次”启动它以加载现在当前的文件,而不是它在几百毫秒前尝试加载的过时文件。

我以下列方式开始一个线程:

LoaderThread := CreateThread(nil, 0, Addr(LoadPicture), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);

我在主窗体的 FormCreate 事件中使用了两次(尽管在任何开始时只执行其中一个),并在 GUI 控件的 OnClick 或 OnSelect 事件中使用它来促进控件的所需功能(例如跳到最后一个图像) .

有什么建议?先感谢您! :)

更新:
这是我的一些(几乎所有)源代码:

procedure TMainForm.FormCreate(Sender: TObject);
begin
MainForm.DoubleBuffered := true;
MainJPEG := TJPEGImage.Create;
MainJPEG.ProgressiveDisplay := true;
MainJPEG.Smoothing := true;
MainJPEG.Performance := jpBestQuality;
MainPNG := TPNGObject.Create;
MainGIF := TGIFImage.Create;
AssignFile(Log, '_NyanLog.txt');
CurrentDir := GetCurrentDir;
ExploreCurrentDir;
if CurrentDirHasImages = false then
begin
SetCurrentDir('..');
CurrentDir := GetCurrentDir;
ExploreCurrentDir;
end;
if CurrentDirHasImages = true then
begin
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
if Length(ImagesOfCurrentDir) > 1 then
begin
MainForm.NextButton.Enabled := true;
MainForm.EndButton.Enabled := true;
MainForm.SlideshowButton.Enabled := true;
MainForm.SlideshowIntervalUpDown.Enabled := true;
end;
UpdateTitleBar;
end
else UpdateTitleBar;
end;

procedure ExploreCurrentDir;
var
Over: boolean;
begin
CurrentPos := 0;
Over := false;
ReWrite(Log);
Write(Log, 'blablabla');
if FindFirst(CurrentDir+'\*.*', faAnyFile-faDirectory, Find) = 0 then
begin
CurrentFilename := Find.Name;
DetermineFiletype;
if CurrentFiletype <> UNSUPPORTED then
begin
SetLength(ImagesOfCurrentDir, CurrentPos+1);
ImagesOfCurrentDir[CurrentPos].Filename := CurrentFilename;
ImagesOfCurrentDir[CurrentPos].Filetype := CurrentFiletype;
MainForm.ImagelistComboBox.AddItem(CurrentFilename, nil);
Write(Log, 'blablabla');
CurrentPos := Succ(CurrentPos);
end;
while Over = false do
begin
if FindNext(Find) = 0 then
begin
CurrentFilename := Find.Name;
DetermineFiletype;
if CurrentFiletype <> UNSUPPORTED then
begin
SetLength(ImagesOfCurrentDir, CurrentPos+1);
ImagesOfCurrentDir[CurrentPos].Filename := CurrentFilename;
ImagesOfCurrentDir[CurrentPos].Filetype := CurrentFiletype;
MainForm.ImagelistComboBox.AddItem(CurrentFilename, nil);
Write(Log, 'blablabla');
CurrentPos := Succ(CurrentPos);
end;
end
else
begin
FindClose(Find);
Over := true;
end;
end;
CurrentDirImageCount := Length(ImagesOfCurrentDir);
CurrentDirHasImages := true;
Write(Log, 'blablabla');
end;
if CurrentDirHasImages = false then Write(Log, 'blablabla');
CloseFile(Log);
CurrentPos := 0;
end;

procedure LoadImage; //procedure #1 which should be used in a thread
begin
if CurrentFiletype = BMP then
begin
MainForm.MainImage.Picture := nil;
MainForm.MainImage.Picture.LoadFromFile(CurrentFilename)
end
else
if CurrentFiletype = JPEG then
begin
MainForm.MainImage.Picture := nil;
MainJPEG.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainJPEG);
end
else
if CurrentFiletype = PNG then
begin
MainForm.MainImage.Picture := nil;
MainPNG.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainPNG);
end
else
if CurrentFiletype = GIF then
begin
MainForm.MainImage.Picture := nil;
MainGIF.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainGIF);
end;
end;

procedure NextImage; //the "NextButton" button from the GUI calls this
begin
if CurrentPos < Length(ImagesOfCurrentDir)-1 then
begin
CurrentPos := Succ(CurrentPos);
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
UpdateTitleBar;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
while MainImageIsEmpty = true do
begin
if CurrentPos < Length(ImagesOfCurrentDir)-1 then
begin
CurrentPos := Succ(CurrentPos);
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
UpdateTitleBar;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
end;
if CurrentPos = CurrentDirImageCount-1 then Break;
end;
end;
if CurrentPos = CurrentDirImageCount-1 then
begin
MainForm.NextButton.Enabled := false;
MainForm.EndButton.Enabled := false;
MainForm.SlideshowButton.Enabled := false;
MainForm.SlideshowIntervalUpDown.Enabled := false;
end;
MainForm.PrevButton.Enabled := true;
MainForm.StartButton.Enabled := true;
end;

procedure PrevImage; //called by "PrevButton"
begin
//some code, calls LoadImage
//almost the same logic as above for a backward step among the images
end;

procedure FirstImage; //called by "StartButton"
begin
//some code, calls LoadImage
end;

procedure LastImage; //called by "EndButton"
begin
//some code, calls LoadImage
end;

procedure Slideshow; //procedure #2 which should be used in a thread
begin
while SlideshowOn = true do
begin
SlideshowInterval := MainForm.SlideshowIntervalUpDown.Position*1000;
Sleep(SlideshowInterval);
NextImage; //NextImage calls LoadImage which should be a thread
if CurrentPos = CurrentDirImageCount-1 then SlideshowOn := false;
end;
end;

function MainImageIsEmpty;
begin
if MainForm.MainImage.Picture = nil then MainImageIsEmpty := true
else MainImageIsEmpty := false;
end;

procedure TMainForm.NextButtonClick(Sender: TObject);
begin
NextImage;
end;

procedure TMainForm.PrevButtonClick(Sender: TObject);
begin
PrevImage;
end;

procedure TMainForm.StartButtonClick(Sender: TObject);
begin
FirstImage;
end;

procedure TMainForm.EndButtonClick(Sender: TObject);
begin
LastImage;
end;

procedure TMainForm.SlideshowButtonClick(Sender: TObject);
begin;
if SlideshowOn = false then
begin
SlideshowOn := true;
SlideshowThread := BeginThread(nil, 0, Addr(Slideshow), nil, 0, SlideshowThreadID);
SlideshowButton.Caption := '||';
SlideshowButton.Hint := 'DIAVETÍTÉS LEÁLLÍTÁSA';
end
else
begin
SlideshowOn := false;
CloseHandle(SlideshowThread);
SlideshowButton.Caption := '|>';
SlideshowButton.Hint := 'DIAVETÍTÉS INDÍTÁSA';
end;
end;

最佳答案

这里文字很多,代码不多。使用更多代码和更少文本,您的问题可能会更好。

无论如何,我可以提供一些提示。

首先拨打CreateThread在 Delphi 中直接进行线程处理是一种相当费力的方法。使用更方便 TThread它以一种更符合典型 Delphi 代码风格的方式解决了一些低级 Windows API 问题。当然,你可以更进一步,使用像 OmniThreadLibrary 这样的线程库。 ,但现在最好坚持使用 TThread并找出如何做到这一点。

现在,这不会是你的问题。几乎可以肯定,您的问题是由线程的两个常见问题之一引起的:

  • 所有 VCL 和 GUI 代码都应该在主线程中运行。 Windows 控件与创建它们的线程具有亲缘关系。 VCL 的许多部分不是线程安全的。这些问题强烈地促使您将所有 VCL/GUI 代码放在主线程中。
  • 由于缺乏同步,您很可能会遇到竞争条件。

  • 处理问题 1 的最常见方法是调用 TThread.SynchronizeTThread.Queue以强制所有 VCL/GUI 代码在主线程上运行。当然,您需要确保工作线程中的所有耗时代码都不会使用 VCL/GUI 对象,因为这注定会失败。

    问题 2 可以通过使用 InterlockedXXX 系列函数的同步对象(如临界区或无锁方法)来处理。

    你的问题到底是什么我不能说。如果您需要更详细的帮助,请发布更多代码,最有可能减少您当前运行的代码。

    关于multithreading - 图像浏览程序 - 为什么它在线程化后会随机崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6885698/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com