如何使任务等待相同的结果?

时间:2019-04-04 14:41:45

标签: c# multithreading async-await

我有一个简单的单例课程,

public class SimpleSingleton
{
    public async Task<int> GetRefreshedValue()
    {
        /*
            What goes here?
        */
        return await GetRefreshedValueImplementation();
        /*
            What goes here?
        */
    }

    private async Task<int> GetRefreshedValueImplementation()
    {
        /*
           Resource intensive and not thread safe
        */
    }
}

由于这是一个单例,因此GetRefreshedValue将被同时调用。我希望一次执行一个或零个任务GetRefreshedValueImplementation

这很简单,我可以使用SemaphoreSlim

private static SemaphoreSlim gate = new SemaphoreSlim(1);
...
await gate.WaitAsync();
try
{
    return await GetRefreshedValueImplementation();
}
finally
{
    gate.Release();
}

但是,我希望每个正在门口等待的任务都获得最近计算出的返回值。我不希望他们排队等候打电话。

编写该代码的最佳方法是什么?

4 个答案:

答案 0 :(得分:2)

因此,操作本身非常简单。您只需要在操作开始时存储Task并在操作完成后清除它,以便可以在任务运行时重新使用它。从那里开始,只是添加适当的同步,以便可以安全地在多个线程中使用(我认为是必需的,并且这并不需要全部通过单个同步上下文进行,如果需要,则可以删除锁定代码。)

public class Foo<T> //TODO come up with good name
{
    private Func<Task<T>> factory;
    private Task<T> currentInvocation;
    private object key = new object();
    public Foo(Func<Task<T>> factory)
    {
        this.factory = factory;
    }
    public Task<T> Value
    {
        get
        {
            lock (key)
            {
                if (currentInvocation == null)
                {
                    currentInvocation = factory();
                    currentInvocation?.ContinueWith(_ =>
                    {
                        lock (key) { currentInvocation = null; }
                    });
                }
                return currentInvocation;
            }
        }
    }
}

答案 1 :(得分:2)

public class SimpleSingleton
{
    private static Task<int> executingTask;
    private static object lockObject = new object();

    public async Task<int> GetRefreshedValue()
    {
        lock (lockObject)
            {
                if (executingTask == null || executingTask.IsCompleted)
                {
                    executingTask = GetRefreshedValueImplementation();
                }
            }
        return await executingTask;
    }

    private async Task<int> GetRefreshedValueImplementation()
    {
        /*
           Resource intensive and not thread safe
        */
    }
}

答案 2 :(得分:0)

据我对您的情况的了解,您需要让电话获得相同正在进行的任务的结果,如果没有,则应创建一个新的电话。如果是这样,这将满足您的目的:

public class SimpleSingleton
{
    private SimpleSingleton() { }
    private static SimpleSingleton _instance;
    public static SimpleSingleton Instance => _instance ?? (_instance = new SimpleSingleton());
    public async Task<int> GetRefreshedValue()
    {
        return await GetRefreshedValueImplementation();
    }
    private volatile Task<int> _getRefreshedValueImplementationTask;
    private Task<int> GetRefreshedValueImplementation()
    {
        if (_getRefreshedValueImplementationTask is null || _getRefreshedValueImplementationTask.IsCompleted)
        {
            return _getRefreshedValueImplementationTask = Task.Run(async () =>
            {
                /*
                   Resource intensive and not thread safe
                */
                int r = new Random().Next(1000, 2001);
                await Task.Delay(r);
                return r;
            });
        }
        return _getRefreshedValueImplementationTask;
    }
}

答案 3 :(得分:-1)

类似的东西:

public class SimpleSingleton
{
    private int _sequenceNo;
    private int _lastvalue;
    private object _lock = new object;

    public async Task<int> GetRefreshedValue()
    {
        var currentSeq = _sequenceNo;
        lock(_lock)
        {
           if (_sequenceNo == currentSeq)
           {
              _lastValue = await GetRefreshedValueImplementation();
              _sequenceNo++;
           }
        }
        return _lastValue;
    }

    private async Task<int> GetRefreshedValueImplementation()
    {
        /*
           Resource intensive and not thread safe
        */
    }
}