1

I have a method in a Singleton class which will be called from different threads. But I need to execute them one by one. like

The method ImageUtil.Instance.LoadImage(imageID) will be called from multiple threads. But I want to load image one by one. So at a time only one image will load.

public class ImageUtil
{
    #region Singleton Implementation
    private ImageUtil()
    {
        taskList = new List<Task<object>>();
    }

    public static ImageUtil Instance { get { return Nested.instance; } }

    private class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as before field init
        static Nested()
        {
        }

        internal static readonly ImageUtil instance = new ImageUtil();
    }

    #endregion

    Queue<Task<Object>> taskList;
    bool isProcessing;
    public async Task<Object> LoadImage(String imageID)
    {
        //Here what I need to put to execute "return await LoadImageInternal(imageID);"
        //one by one. So that if one image is loading and mean time some other thread
        //calls this method then the last thread have to wait until current loading finish.

    }
    private async Task<Object> LoadImageInternal(String imageID)
    {
        //Business Logic for image retrieval.
    }
}
Douglas
  • 53,759
  • 13
  • 140
  • 188
gmtek
  • 741
  • 3
  • 7
  • 25

3 Answers3

6

SemaphoreSlim has a WaitAsync method that allows you to enforce critical sections asynchronously:

private readonly SemaphoreSlim loadSemaphore = new SemaphoreSlim(1, 1);

public async Task<Object> LoadImage(String imageID)
{
    await loadSemaphore.WaitAsync();

    try
    {
        return await LoadImageInternal(imageID);
    }
    finally
    {
        loadSemaphore.Release();
    }
}

This pattern is presented in Stephen Toub's article.

Douglas
  • 53,759
  • 13
  • 140
  • 188
  • 1
    Right, and the semaphore has the queue built in that the OP manually wrote. – usr Nov 27 '15 at 21:52
  • I think this is the best solution instead of implementing own queue and synchronous handing. – gmtek Nov 27 '15 at 22:14
0
List<Task<Object>> taskList;
private static readonly object _syncLock = new object();
public Task<Object> LoadImage(String imageID)
{
    return Task<Object>.Factory.StartNew(() =>
    {
        lock (_syncLock)
        {
            return LoadImageInternal(imageID).Result;
        }
    });
}
private async Task<Object> LoadImageInternal(String imageID)
{
    //Business Logic for image retrieval.
}

That should accomplish what you asked for, but personally I would tackle this differently with a long-running task and a Queue of some sort. The long running task would simply loop forever and check the Queue for new items and then execute them one at a time, this would prevent a lot of unnecessary thread context switching.

caesay
  • 16,932
  • 15
  • 95
  • 160
  • 1
    Suggesting `.Result` without pointing out to problems with it is not really nice thing to do... Especially with I/O operation under shared lock. – Alexei Levenkov Nov 27 '15 at 20:42
  • I agree with @caesay that using a `Queue` is a good idea. I have implemented that successfully several times. – Brian Payne Nov 27 '15 at 20:47
  • 1
    @AlexeiLevenkov: I'm not sure of what problems you're referring to. Only thing I've heard of is an issue of using `.Result` from an `async void`, and `LoadImage` is not async or void... – caesay Nov 27 '15 at 21:00
  • This nullifies the async design which, presumably, is there for a good reason. – usr Nov 27 '15 at 21:33
  • @usr: This was the design before async/await was introduced. – caesay Nov 27 '15 at 21:34
  • @caesay I have edited the question as per your suggestion. But how to return the result properly. – gmtek Nov 27 '15 at 21:38
  • @Gulshan: You should use Douglas's answer. It is better than mine I think. – caesay Nov 27 '15 at 21:39
  • 1
    This has nothing to do with await. Calling Result nullifies the benefits of async IO because the whole point is to not block. Await is just a convenience to work with async IO in a non-blocking way. – usr Nov 27 '15 at 21:51
0
//This is how you can implement it using yield return to return one image at a time
public IEnumerable<Task<string>> GetPerItemAsync(IEnumerable<string> images)
{
    foreach (var image in images)
    {
       yield return LoadImage(image);
    }
 }

 public static Task<string> LoadImage(string image)
 {
     var result = image.Trim(); // Some complex business logic on the image, NOT

     return new Task<string>(() => result);
  }

  //Call your method in you client
  //First, get your images from the data source
  var listOfimages = Context.Images.ToList();

  //Get results
  var result = GetPerItemAsync(listOfimages).FirstOrDefault();
Julius Depulla
  • 1,493
  • 1
  • 12
  • 27