gmtek
gmtek

Reputation: 811

Synchronous Task Execution in C#

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.
    }
}

Upvotes: 1

Views: 391

Answers (3)

Julius Depulla
Julius Depulla

Reputation: 1633

//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();

Upvotes: 0

Douglas
Douglas

Reputation: 54907

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.

Upvotes: 6

caesay
caesay

Reputation: 17233

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.

Upvotes: 0

Related Questions