2015-12-23 2 views
1
private Data GetDefaultData() 
{ 
    var data = Task.Factory.StartNew(() => GetData()); 
    return data.Result; 
} 

Если GetData() выполняет в 100 мс, и я запускаю один GetDefaultData() за 10 мс. Правильно ли, что первые 10 вызовов будут использовать те же data.Result? GetData() collect Data внутри lock заявление. Если не как изменить код, чтобы предоставить эту возможность?Как кэшировать данные при выполнении задачи?

+0

С учетом предоставленного кода для каждого вызова функции GetDefaultData() будет развернута другая задача. Независимо от того, является ли значение 'data.Result' одинаковым между вызовами, зависит от того, что происходит внутри' GetData' –

+0

GetData() собирает данные внутри оператора блокировки. –

+0

Вы хотите позвонить один раз и прочитать много? Или вы хотите, чтобы данные обновлялись после X миллисекунд? –

ответ

2

Скажем, у нас есть первый вызов GetDefaultData (GetData() выполняется в 100 мс), а затем у нас есть 10 вызовов (GetDefaultData() за 10 мс). Я хочу, чтобы этот остаток звонков получил тот же ответ, что и первый.

Похоже, вы хотите класс Lazy<T>.

public class YourClass 
{ 
    private readonly Lazy<Data> _lazyData; 

    public YourClass() 
    { 
     _lazyData = new Lazy<Data>(() => GetData()); 
    } 

    private Data GetDefaultData() 
    { 
     return _lazyData.Value; 
    } 

    public Data GetData() 
    { 
     //... 
    } 
} 

Первый поток для вызова GetDefaultData() будет работать GetData(), когда она попадает _lazyData.Value, все остальные из потоков будет блокировать на вызов _lazyData.Value до первой резьбы отделок и использовать результат от вызова этого первого потока. GetData() будет вызываться только один раз.

Если вы не хотите блокировать вызов, вы можете легко сделать класс AsyncLazy<T>, который использует потоки внутри.

public class AsyncLazy<T> : Lazy<Task<T>> 
{ 
    public AsyncLazy(Func<T> valueFactory) : 
     base(() => Task.Run(valueFactory)) 
    { 
    } 

    public AsyncLazy(Func<Task<T>> taskFactory, bool runFactoryInNewTask = true) : 
     base(() => runFactoryInNewTask ? Task.Run(taskFactory) : taskFactory()) 
    { 
    } 

    //This lets you use `await _lazyData` instead of doing `await _lazyData.Value` 
    public TaskAwaiter<T> GetAwaiter() 
    { 
     return Value.GetAwaiter(); 
    } 
} 

Тогда ваш код становится (я также сделал GetData функцию асинхронной тоже, но перегруженные AsyncLazy пусть это будет либо или)

public class YourClass 
{ 
    private readonly AsyncLazy<Data> _lazyData; 

    public YourClass() 
    { 
     _lazyData = new AsyncLazy<Data>(() => GetData(), false); 
    } 

    private async Task<Data> GetDefaultData() 
    { 
     //I await here to defer any exceptions till the returned task is awaited. 
     return await _lazyData; 
    } 

    public Task<Data> GetData() 
    { 
     //... 
    } 
} 

EDIT: Там некоторые возможные проблемы с AsyncLazy , see here.

+1

Важно отметить две вещи. Во-первых, тот факт, что эта реализация «AsyncLazy» не будет распространять исключения в случае, если это происходит, а IsValueCreated будет установлен на «false». Кроме того, 'Lazy ' является проблематичным в отношении исключений вообще. Он будет кэшировать и реконструировать их в случае, если заводский метод выбрасывает. Это может быть проблематично, если этот ленивый делает сетевой IO. –

+0

Подробнее о первой проблеме может быть [найдено здесь] (http://stackoverflow.com/questions/33872588/caching-the-result-from-an-async-factory-method-iff-it-doesnt-throw/33874601 # 33874601) –

+1

@YuvalItzchakov Спасибо за информацию! Я обновил ответ на ссылку на другой вопрос. –

0

Короче говоря: Нет

Каждый раз, когда вы звоните GetDefaultData() новая задача запускается, так Data.Result будет оставаться неизменным в течение всего срока GetData(), а затем содержат то, что вы назначили ему в GetData(). Также возврат значения из нового объекта Task не принесет вам пользы - так многозадачность работает. Ваш код будет продолжать выполняться в основном потоке, но значение результата будет установлено только после завершения отдельной задачи. Является ли он содержит инструкции блокировки или нет.

+0

Что мне нужно изменить для кеширования? сценарий, который я описываю в вопросе –

+0

Это зависит от того, что действительно делает GetData. Зачем вам нужны задачи в первую очередь? Что вы делаете с результатом = – srandppl

+0

Мне нужно обрабатывать параллельный запрос, ожидая результат уже запущенной операции (мой собственный кеш). –

0

Возможно, вам понравится ReaderWriterLock. ReaderWriterLock используется для синхронизации доступа к ресурсу. В любой момент времени он позволяет одновременно использовать доступ для чтения для нескольких потоков или записывать доступ для одного потока. Эта логика должна быть добавлена ​​в ваш метод GetData, вероятно, поэтому, чтобы в определенный тайм-аут он мог либо использовать writelock, либо удерживать его в течение этого времени ожидания, в противном случае использовать операцию чтения.