Image of a retriever in the stream

I am trying to make an image retriever to work with a list.

The list contains items of type (TItem), for example. TItem has some properties like title, image and imageURL.

There is a combo box thread that scans all items and tries to get an image of each item using the imageURL of each item.

The stream that retrieves the image of each item works like this:

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;

      

Unfortunately, it doesn't work in one case: when the list is flushed and that the item image is being fetched by a stream.

(All reading and writing of elements is protected by a mutex).

What should I do?

Thank:)

+2


source to share


2 answers


The main problem is that your code doesn't protect the loop itself using a mutex. As you probably figured out, this will create a huge mutex that will slow down the system significantly.

Here's a good solution:

  • Replace for loop with while loop
  • Create a code that finds the following url and mutex-protect that code
  • Make the image search use variables that are not part of the list, so it doesn't need to be protected by a mutex.
  • Save the resulting image with the correct URL using the URL. This detection and storage must be protected by a mutex.


Something like that:

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;

      

Add the necessary mutex code, try-statements, etc.

+3


source


There are many ways to solve this, here are two examples:

  • Don't use a list of objects, use a TInterfaceList

    general list of interfaces or. Create an interface from the public methods of the item class. The thread will maintain a reference to the interface, keeping the count above zero, and thus the object instance that implements the interface will not be deleted. Thus, access to the item will be safe.

  • You cannot directly access an element from a stream, but only an opaque element must be in the stream. Initially, the thread will use this handle to request the data needed to retrieve the image, and since it will block the list, access is safe. When the image is restored, the thread will again use the handle to set the image to an element in the locked section of code. If the element is no longer valid, the handle will not resolve the element and the extracted image will simply be removed. You only need to make sure the handles are not reused, so for example the index of a list or the address of an element would be bad ideas. An integer to be incremented for each OTOH item will work well.

Simplified code for the second way:



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;

      

You can even use the image url as a descriptor.

Note that the best way would be to block the thread if the list is empty, your call Sleep()

is basically a poll. Not much overhead, but still bad style.

+4


source







All Articles