1

I have a slow and expensive method that return some data for me:

public Data GetData(){...}

I don't want to wait until this method will execute. Rather than I want to return a cached data immediately.

I have a class CachedData that contains one property Data cachedData. So I want to create another method public CachedData GetCachedData() that will initiate a new task(call GetData inside of it) and immediately return cached data and after task will finish we will update the cache.

I need to have thread safe GetCachedData() because I will have multiple request that will call this method.

I will have a light ping "is there anything change?" each minute and if it will return true (cachedData != currentData) then I will call GetCachedData().

I'm new in C#. Please, help me to implement this method.

I'm using .net framework 4.5.2

Anatoly
  • 1,908
  • 4
  • 25
  • 47
  • Maybe you could use the "out" keyword and return the data before the Task begins. And once the Task is completed update your cache. Or a better solution would be to separate your `GetData` method from the long running Task. – Transcendent Dec 08 '15 at 13:09
  • If `Data` is in fact a kind of *cursor* you may try return `IEnumerable` with (buffrered - `BlockingCollection` - if necessary) `yield return` – Dmitry Bychenko Dec 08 '15 at 13:13
  • @DmitryBychenko: In that case the method may only not be executed until assigned with a variable. So I think this may not solve this guy's problem as he wants to return something in the beginning of the method's operation rather than the end of it. – Transcendent Dec 08 '15 at 13:14
  • 1
    You can return a *promise* of data (which is normally a `Task<>`). Question is how you want to obtain valid data. Polling? Event? Sounds like `async/await` to me. `Task.IsCompleted` is thread-safe. – Sinatr Dec 08 '15 at 13:18
  • @Transcendent Your solution doesn't work either. The method will not return to caller until it is finished. There is no difference of using out and returning an object. The only way will be to create a task inside this method and return an object that informs about the end. You can return a `Task` or any other object that will raise an event if a result is available. – Sebastian Schumann Dec 08 '15 at 13:20
  • @Verarind: If it was me, I'd return that cached data by defining a read-only property. Because logically there's no point getting the cached using that method. That method instead can run periodically using another Task to cache the data like in every 30 min (for example). – Transcendent Dec 08 '15 at 13:22
  • @Sinatr I will send a ping "is there anything change?" each minute If it will return true(cached != current) then I will call `GetCachedData` – Anatoly Dec 08 '15 at 13:32
  • 3
    What have you tried implementing? Sounds like you have a half way design of what you want. Try it out, and let us know if you run into problems. – Yuval Itzchakov Dec 08 '15 at 13:36
  • 1
    @YuvalItzchakov I said that I trying to implement GetCachedData – Anatoly Dec 08 '15 at 13:43
  • Show us your effort. – Yuval Itzchakov Dec 08 '15 at 13:46
  • Implement CachedData as static. Call GetCachedData which runs GetData asynchronously and returns CacheData. On GetData use lock while u are updating data. If CacheData is null wait GetData(Task) until finishes then return CacheData (This will needed for first run). – Erkan Demirel Dec 08 '15 at 13:50

3 Answers3

2

The basic idea is clear:

  • You have a Data property which is wrapper around an expensive function call.
  • In order to have some response immediately the property holds a cached value and performs updating in the background.
  • No need for an event when the updater is done because you poll, for now.

That seems like a straight-forward design. At some point you may want to use events, but that can be added later.

Depending on the circumstances it may be necessary to make access to the property thread-safe. I think that if the Data cache is a simple reference and no other data is updated together with it, a lock is not necessary, but you may want to declare the reference volatile so that the reading thread does not rely on a stale cached (ha!) version. This post seems to have good links which discuss the issues.

Community
  • 1
  • 1
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
1

If you will not call GetCachedData at the same time, you may not use lock. If data is null (for sure first run) we will wait long method to finish its work.

public class SlowClass
{
    private static object _lock;
    private static Data _cachedData;
    public SlowClass()
    {
        _lock = new object();
    }

    public void GetCachedData()
    {
        var task = new Task(DoStuffLongRun);
        task.Start();
        if (_cachedData == null)
            task.Wait();
    }

    public Data GetData()
    {
        if (_cachedData == null)
            GetCachedData();
        return _cachedData;
    }
    private void DoStuffLongRun()
    {

        lock (_lock)
        {
            Console.WriteLine("Locked Entered");
            Thread.Sleep(5000);//Do Long Stuff
            _cachedData = new Data();
        }

    }
}

I have tested on console application.

 static void Main(string[] args)
    {

        var mySlow = new SlowClass();
        var mySlow2 = new SlowClass();
        mySlow.GetCachedData();
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(i);
            mySlow.GetData();
            mySlow2.GetData();
        }
        mySlow.GetCachedData();
        Console.Read();
    }
Erkan Demirel
  • 4,302
  • 1
  • 25
  • 43
-1

Maybe you can use the MemoryCache class, as explained here in MSDN

AllWorkNoPlay
  • 454
  • 1
  • 4
  • 20