Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 4103574
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 20, 20262026-05-20T20:58:33+00:00 2026-05-20T20:58:33+00:00

I need to synchronize a sequence of operations that contains an asynchronous part. The

  • 0

I need to synchronize a sequence of operations that contains an asynchronous part.
The method looks into an image cache and returns the image if it’s there (invokes a callback in reality). Otherwise it has to download it from the server. The download operation is asynchronous and fires an event on completion.

This is the (simplified) code.

private Dictionary<string, Bitmap> Cache;

public void GetImage(string fileName, Action<Bitmap> onGetImage)
{
    if (Cache.ContainsKey(fileName))
    {
        onGetImage(Cache[fileName]);
    }
    else
    {
        var server = new Server();
        server.ImageDownloaded += server_ImageDownloaded;
        server.DownloadImageAsync(fileName, onGetImage); // last arg is just passed to the handler
    }
}

private void server_ImageDownloaded(object sender, ImageDownloadedEventArgs e)
{
    Cache.Add(e.Bitmap, e.Name);
    var onGetImage = (Action<Bitmap>)e.UserState;
    onGetImage(e.Bitmap);
}

The problem: if two threads call GetImage almost at the same time, they will both call the server and try to add the same image to the cache. What I should do is create lock at the beginning of GetImage and release it at the end of the server_ImageDownloaded handler.

Obviously this is not doable with the lock construct and it would not make sense, because it would be difficult to ensure that the lock is realeased in any case.

Now what I thought I could do is use a lambda instead of the event handler. This way I can put a lock around the whole section:

I have to lock the Cache dictionary at the beginning of the DownloadImage method and release it only at the end of the ImageDownloaded event handler.

private Dictionary<string, Bitmap> Cache;

public void GetImage(string fileName, Action<Bitmap> onGetImage)
{
    lock(Cache)
    {
        if (Cache.ContainsKey(fileName))
        {
            onGetImage(Cache[fileName]);
        }
        else
        {
            var server = new Server();
            server.ImageDownloaded += (s, e) =>
            {
                Cache.Add(e.Bitmap, e.Name);
                onGetImage(e.Bitmap);
            }
            server.DownloadImageAsync(fileName, onGetImage); // last arg is just passed to the handler
        }
    }
}

Is this safe? Or the lock is immediately released after execution of GetImage, leaving the lambda expression unlocked?

Is there a better approach to solve this problem?


SOLUTION

In the end the solution was a bit of a mix of all the answers and comments, unfortunately I cannot mark-as-answer all of them. So here is my final code (removed some null checks/error cases/etc. for clarity).

private readonly object ImageCacheLock = new object();
private Dictionary<Guid, BitmapImage> ImageCache { get; set; }
private Dictionary<Guid, List<Action<BitmapImage>>> PendingHandlers { get; set; }

public void GetImage(Guid imageId, Action<BitmapImage> onDownloadCompleted)
{
    lock (ImageCacheLock)
    {
        if (ImageCache.ContainsKey(imageId))
        {
            // The image is already cached, we can just grab it and invoke our callback.
            var cachedImage = ImageCache[imageId];
            onDownloadCompleted(cachedImage);
        }
        else if (PendingHandlers.ContainsKey(imageId))
        {
            // Someone already started a download for this image: we just add our callback to the queue.
            PendingHandlers[imageId].Add(onDownloadCompleted);
        }
        else
        {
            // The image is not cached and nobody is downloading it: we add our callback and start the download.
            PendingHandlers.Add(imageId, new List<Action<BitmapImage>>() { onDownloadCompleted });
            var server = new Server();
            server.DownloadImageCompleted += DownloadCompleted;
            server.DownloadImageAsync(imageId);
        }
    }
}

private void DownloadCompleted(object sender, ImageDownloadCompletedEventArgs e)
{
    List<Action<BitmapImage>> handlersToExecute = null;
    BitmapImage downloadedImage = null;

    lock (ImageCacheLock)
    {
        if (e.Error != null)
        {
            // ...
        }
        else
        {
            // ...
            ImageCache.Add(e.imageId, e.bitmap);
            downloadedImage = e.bitmap;
        }

        // Gets a reference to the callbacks that are waiting for this image and removes them from the waiting queue.
        handlersToExecute = PendingHandlers[imageId];
        PendingHandlers.Remove(imageId);
    }

    // If the download was successful, executes all the callbacks that were waiting for this image.
    if (downloadedImage != null)
    {
        foreach (var handler in handlersToExecute)
            handler(downloadedImage);
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-20T20:58:35+00:00Added an answer on May 20, 2026 at 8:58 pm

    Why don’t you just keep a a collection of image filenames that are being downloaded, and have the code for a thread be:

    public void GetImage(string fileName, Action<Bitmap> onGetImage) 
    { 
        lock(Cache) 
        { 
            if (Cache.ContainsKey(fileName)) 
            { 
                onGetImage(Cache[fileName]); 
            } 
            else if (downloadingCollection.contains(fileName))
            {
                while (!Cache.ContainsKey(fileName))
                {
                    System.Threading.Monitor.Wait(Cache)
                }
                onGetImage(Cache[fileName]); 
            }
            else 
            { 
               var server = new Server(); 
               downloadCollection.Add(filename);
               server.ImageDownloaded += (s, e) => 
               { 
                  lock (Cache)
                  {
                      downloadCollection.Remove(filename);
                      Cache.Add(e.Bitmap, e.Name); 
                      System.Threading.Monitor.PulseAll(Cache);
                  }
                  onGetImage(e.Bitmap);
    
               } 
               server.DownloadImageAsync(fileName, onGetImage); // last arg is just passed to the handler 
            } 
        } 
    }
    

    That is more or less the standard monitor pattern, or would be if you refactored the lambda expression into a member function like GetImage. You should really do that. It will make the monitor logic easier to reason about.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I just realized that I need to synchronize a significant amount of data collection
Need a function that takes a character as a parameter and returns true if
Need to an expression that returns only things with an I followed by either
Need a function like: function isGoogleURL(url) { ... } that returns true iff URL
I need to replicate a sequence/counter across an eight node cluster. This means that
I am novice on cloud and i need to synchronize my data(Contact,image,video) on cloud.
I need to synchronize two tables. Let's assume that the tables contain following columns:
do i need to synchronize this, when many threads accessing the get Method and
Is there any free MySQL synchronization tool out there? I need to synchronize the
I was wonder about the proper usage of ManualWorkflowSchedulerService.RunWorkflow(). Do I need to synchronize

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.