C#中的同步任务执行

Synchronous Task Execution in C#

我在 Singleton class 中有一个方法,它将被不同的线程调用。但是我需要一个一个地执行它们。喜欢

方法 ImageUtil.Instance.LoadImage(imageID) 将从多个线程调用。但我想一张一张地加载图像。所以一次只会加载一张图片。

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

这应该可以完成您的要求,但就我个人而言,我会用长 运行 任务和某种 Queue 来以不同的方式解决这个问题。长 运行 任务将永远循环并检查队列中是否有新项目,然后一次执行一个项目,这将防止大量不必要的线程上下文切换。

SemaphoreSlim 有一个 WaitAsync 方法可以让你异步执行临界区:

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

此模式出现在 Stephen Toub's article

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