我正在尝试使用图像检索器来处理列表。
该列表包含类型(TItem)的项目。 TItem有一些属性,如title,image和imageURL。
列表中有一个线程扫描所有项目,并尝试使用每个项目的imageURL检索每个项目的图像。
检索每个项目图像的线程的工作方式如下:
while not terminated do
begin
for i := 0 to List.count-1 do
begin
item := List.Items[i];
//Note : it can takes a few sec to retrieve the image from the imageURL. This method
//retrieve the image from the item.imageURL and then assign it to item.image
RetrieveImage(item.imageURL, item.Image);
end;
sleep(100);
end;
不幸的是,它在一种情况下不起作用:清除列表并且线程正在检索项目的图像。
(所有读写项目均由互斥锁保护。)
我该怎么办?
谢谢:)
答案 0 :(得分:4)
有很多方法可以解决这个问题,这里有两个例子:
不要使用对象列表,使用TInterfaceList
或通用的接口列表。从项类的公共方法创建接口。该线程将维护一个接口引用,使引用计数保持在零以上,因此不会删除实现该接口的对象实例。因此,访问该项目是安全的。
不要直接从您的线程访问该项目,但只为该线程提供一个不透明的项目句柄。最初,线程将使用该句柄来请求获取图像所需的数据,并且由于它将锁定列表,因此访问是安全的。检索图像时,线程将再次使用句柄将图像设置为锁定的代码段中的项目。如果该项目不再有效,则句柄将不会解析为项目,并且将仅删除检索到的图像。您只需确保不重复使用句柄,因此例如列表索引或项目的地址都是错误的想法。对于每个项目OTOH将递增的整数可以正常工作。
第二种方式的简化代码:
var
Img: TImage;
ImgHandle: TImageFromURLHandle;
...
Img := TImage.Create;
try
while not Terminated do
begin
// GetNextImageURL() is thread-safe
while List.GetNextImageURL(ImgHandle, ImgURL) do begin
RetrieveImage(ImgURL, Img);
// SetImage() is thread-safe and will do nothing if the image item
// is no longer in the list (invalid handle)
List.SetImage(ImgHandle, Img);
end;
Sleep(100);
end;
finally
Img.Free;
end;
您甚至可以将图片网址本身用作句柄。
请注意,如果列表为空,则更好的方法是阻止线程,Sleep()
调用基本上是轮询。没有多少开销,但仍然是糟糕的风格。
答案 1 :(得分:3)
基本问题是您的代码不使用互斥锁来保护循环本身。正如您可能已经意识到的那样,这会产生巨大的互斥量,从而显着降低系统速度。
这是一个很好的解决方案:
这样的事情:
while not terminated do
begin
currenturl:='';
while true do begin
Mutex begin
currenturl:=FindNextUrl(currentUrl);
Mutex end
if currenturl='' then break; // No more URLs to be found
RetrieveImage(currenturl,image);
Mutex begin
index:=FindUrlIndex(currenturl)
List[index].image:=image;
Mutex end
end;
sleep(100);
end;
自己添加必要的互斥代码,try-statements等。