2015-01-03 5 views
0

Я пытаюсь интегрировать стороннюю библиотеку в моем приложении WPF. Эта библиотека используется для сбора истории кредитной карты для человека по его номеру социального страхования. Он в основном предоставляет метод, который возвращает void. Как только этот метод будет завершен, вы сможете собрать историю кредитных карт.Нужны предложения по эффективному дизайну для моего случая использования

ThirdPartyLibrary Instance = new Instance(SSN); 
Instance.Run(); // This method returns a void and it blocks until data retrieval completes 
if (Instance.Success) 
    CreditHistory History = Instance.CreditHistory; 

Теперь я хочу, чтобы запустить этот API для числа лиц, одновременно, однако, я также хочу иметь порог максимального количества запросов одновременно. Если этот порог поражен, я хотел бы дождаться завершения одного из этих запросов перед отправкой нового запроса. Наконец, я хотел бы предоставить обновление моего пользовательского интерфейса, поскольку любой из представленных запросов будет успешным или не удастся.

Вот мои вопросы:

  1. Эта партия API третий может в конечном итоге ждет довольно долго для сбора информации. Использует ли API TPL/PLINQ, например Parallel.ForEach или AsParallel хорошим кандидатом на эту ситуацию?

  2. Могу ли я использовать await/async для создания обертки вокруг звонков в стороннюю библиотеку, учитывая, что его метод Run возвращает пустоту?

  3. Что было бы лучше всего подождать, когда будет отправлен индивидуальный запрос, чтобы я мог обновлять свой интерфейс пользователя о прогрессе.

Просьба представить ваши предложения (с некоторым примером кода) о том, как его следует разрабатывать/внедрять? Я много читал в Tasks и Async/Await, но довольно запутался, как лучшие образцы для этого варианта использования?

+0

Вы можете использовать блокирующую коллекцию с верхней границей http://msdn.microsoft.com/en-us/library/dd267301(v=vs.110) .aspx – Paparazzi

+0

Спасибо Бальзам за ваше предложение. Это будет использование шаблона производителя-потребителя, верно? Если это так, я читал об этом также, но я не уверен, как интегрировать его с этой сторонней библиотекой, а также получать обновление о достигнутом прогрессе. Есть идеи? –

+0

В основном я читал/играл с меньшими примерами кода, чтобы понять понятия. Например, я читал в нескольких местах, что Parallel.ForEach не будет хорошим кандидатом для любых операций, которые делают I/O. Этот сторонний API вызывает некоторые веб-службы для извлечения данных, поэтому я просто хотел получить мысли о том, какой подход я должен использовать для начала в моем проекте. –

ответ

1

Как отметил @ l3arnon, ваш API синхронно (хотя он выполняет операции на основе ввода/вывода, которые естественно асинхронны). Таким образом, вам нужно будет рассматривать это как блокирование.

Не зная больше о ваших требованиях, я бы рекомендовал async с Task.Run. Вы также можете использовать Parallel или Parallel LINQ (AsParallel), но они лучше всего работают, когда вы знаете общее количество операций в начале, и неясно, так ли это в этом случае. Кроме того, если вам нужно добавить операции, в то время как другие находятся в полете, вы также можете рассмотреть TPL Dataflow и Rx. ИМО, подход async имеет самую низкую кривую обучения. Тем не менее, Rx, в частности, имеет очень элегантный дизайн, и как только вы его узнаете, вы начнете использовать его повсюду.

Прежде всего, вы, вероятно, захотите использовать метод утилиты, который использует естественные типы возврата и исключения для обработки ошибок. Я не знаю, почему ваш класс третьей стороной уже не есть что-то вроде этого:

CreditHistory GetCreditHistory(string ssn) 
{ 
    var instance = new Instance(ssn); 
    instance.Run(); 
    if (instance.Success) 
    return instance.CreditHistory; 
    throw ... 
} 

В async случае, вы бы начать писать метод, который должен вызываться из потока пользовательского интерфейса, и выполняет работу в фоновом режиме:

async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn) 
{ 
    try 
    { 
    var history = await Task.Run(() => GetCreditHistory(ssn)); 
    // Update UI with credit history. 
    } 
    catch (Exception ex) 
    { 
    // Update UI with error details. 
    } 
} 

Тогда вы можете добавить асинхронное дросселирование. Это один подход, используя встроенный в SemaphoreSlim:

private readonly SemaphoreSlim _mutex = new SemaphoreSlim(10); 
async Task GetCreditHistoryOnBackgroundThreadAsync(string ssn) 
{ 
    await _mutex.WaitAsync(); 
    try 
    { 
    var history = await Task.Run(() => GetCreditHistory(ssn)); 
    // Update UI with credit history. 
    } 
    catch (Exception ex) 
    { 
    // Update UI with error details. 
    } 
    finally 
    { 
    _mutex.Release(); 
    } 
} 
+0

Спасибо за ваш ответ. Я узнаю количество запросов, которые данное приложение отправит для извлечения истории кредитной карты. Как это изменить образец кода, который вы указали выше? Мне тоже нравится эта идея. Так что если код значительно изменится, я попрошу добавить его в свой ответ, не удаляя его. –

+0

@PaulSnow: вы можете просто использовать 'await Task.WhenAll' для выполнения нескольких параллельных операций. –

+0

ok, еще раз спасибо –

2

Я не могу сказать, какие из этих методов являются лучшими из лучших.

Вы можете использовать образец из MSDN около Degree of Parallelism. Но для этого потребуется установить пространство имен System.Threading.Tasks.Dataflow.

Parallel класс имеет возможность, в котором можно указать степень параллельности, тоже:

var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 }; 

Parallel.For(0, NumberOfClients, myOptions, i=> 
{ 
    // Code to run 
}); 

Итак, ответ на первый вопрос ДА, список очень хороший кандидат на выполняйте свою задачу.

Ответ на второй вопрос: есть, вы можете.

Последний из них:

На UI Thread вы можете назвать что-то вроде этого:

Task.Factory.StartNew(DoWork); 

Вы можете взять использовать CancellationToken, чтобы поддержать отмену. Вы также можете использовать другие перегрузки метода, чтобы получить требуемое поведение. У вас также может быть свойство Value, которое будет обозначать, сколько записей было выполнено.Это свойство можно связано с ProgressBar, и обновлять его правильно

public void DoWork() 
{ 
    var myOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 }; 

    Parallel.For(0, NumberOfClients, myOptions, i=> 
    { 
     ThirdPartyLibrary Instance = new Instance(SSN[i]); 
     Instance.Run(); 
     Interlocked.Increment(ref value); 
     RaisePropertyChanged("Value"); 
    }); 
} 

Несколько слов о Interlocked.Increment(ref value): он выполняет приращение указанной переменной и сохранить результат, как атомарные операции, для того, чтобы предотвратить the data race condition.

+0

Спасибо stukselbax. Я получаю ошибку компиляции при попытке использовать ожидание с Instance.Run, жалуясь на его пустоту. Вы сказали, что его можно использовать. Что мне не хватает? –

+1

Вероятно, вам следует применить метод блокировки 'Instance.Run()', который будет помечен как [async] (http://msdn.microsoft.com/en-us/library/hh191443.aspx) и использовать это с ключевым словом ожидания. – stukselbax

Смежные вопросы