Создание асинхронного запроса LinQ

67
7

Я проверил и попробовал этот пример: Как написать асинхронный запрос LINQ?

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

Я сделал это, пока я программировал для android, и результат был потрясающим и очень быстрым, но я не знаю, как это сделать на С# и используя linQ

спросил(а) 2015-01-20T13:37:00+03:00 5 лет, 5 месяцев назад
1
Решение
-4

Попробуй еще раз...

Если я понимаю вас, логика, которую вы хотите, это что-то вроде...

var query = getData.Where( ... );
query.AsParallel().ForEach(r => {
//other stuff
});

Что будет здесь...

Короче говоря, компилятор будет оценивать это как-то вроде: в то время как итерация по результатам запроса параллельно выполняет логику в области комментария.

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

Это автоматически управляемая асинхронная параллельная операция.

Также стоит отметить, что если я сделаю это...

var query = getData.Where( ... );

... фактический код не запускается до тех пор, пока я не начну повторять IQueryable и объявив операцию параллельной, которую инфраструктура может работать с несколькими результатами в любой момент времени, набирая код для вас.

ForEach по существу является просто обычным циклом foreach, где каждая итерация выполняется асинхронно.

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

Могу ли я предложить что-то вроде этого:

void DoAsync<T>(IQueryable<T> items, Func<T> operation, Func<T> callback)
{
items.AsParallel().ForEach(x => {
operation(x);
callback(x);
});
}

ответил(а) 2015-01-20T13:52:00+03:00 5 лет, 5 месяцев назад
39

Асинхронные последовательности моделируются в.NET как IObservable s. Reactive Extensions - это асинхронный раздел LINQ.

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

var sequence = Observable.Generate(0,
i => i < 10,
i => i + 1,
i => SomeExpensiveGeneratorFunction());

И затем запросите его, используя все обычные функции LINQ (что в результате позволяет использовать синтаксис запроса), а также ряд дополнительных операций, которые специально предназначены для асинхронной последовательности (а также множество разных способов создания наблюдаемых последовательностей ) такие как:

var query = from item in sequence
where ConditionIsTrue(item)
select item.ToString();

Краткое описание того, что происходит здесь, - это просто сказать, что он делает именно то, что вы хотите. Функция генератора просто уведомляет своих подписчиков всякий раз, когда она успешно генерирует значение (или когда оно выполняется), и продолжает генерировать значения, не дожидаясь завершения подписки, метод Where будет подписываться на sequence и уведомлять своих подписчиков всякий раз, когда он наблюдает за значением, которое проходит условие, Select будет подписываться на последовательность, возвращаемую Where и выполнять ее преобразование (асинхронно) всякий раз, когда она получает значение, а затем подталкивает ее ко всем своим подписчикам.

ответил(а) 2015-01-22T00:44:00+03:00 5 лет, 5 месяцев назад
-4

Я изменил ответ TheSoftwareJedi по вашей ссылке. Вы можете поднять первое событие запуска из класса Asynchronous и использовать его для запуска вашей работы. Здесь класс,

public static class AsynchronousQueryExecutor
{
private static Action<object> m_OnFirstItemProcessed;

public static void Call<T>(IEnumerable<T> query, Action<IEnumerable<T>> callback, Action<Exception> errorCallback, Action<object> OnFirstItemProcessed)
{
m_OnFirstItemProcessed = OnFirstItemProcessed;
Func<IEnumerable<T>, IEnumerable<T>> func =
new Func<IEnumerable<T>, IEnumerable<T>>(InnerEnumerate<T>);
IEnumerable<T> result = null;
IAsyncResult ar = func.BeginInvoke(
query,
new AsyncCallback(delegate(IAsyncResult arr)
{
try
{
result = ((Func<IEnumerable<T>, IEnumerable<T>>)((AsyncResult)arr).AsyncDelegate).EndInvoke(arr);
}
catch (Exception ex)
{
if (errorCallback != null)
{
errorCallback(ex);
}
return;
}
//errors from inside here are the callbacks problem
//I think it would be confusing to report them
callback(result);
}),
null);
}
private static IEnumerable<T> InnerEnumerate<T>(IEnumerable<T> query)
{
int iCount = 0;
foreach (var item in query) //the method hangs here while the query executes
{
if (iCount == 0)
{
iCount++;
m_OnFirstItemProcessed(item);

}
yield return item;
}
}
}

здесь ассоциации,

private void OnFirstItem(object value) // Your first items is proecessed here.
{
//You can start your work here.
}

public void HandleResults(IEnumerable<int> results)
{
foreach (var item in results)
{
}
}

public void HandleError(Exception ex)
{
}


и здесь, как вы должны вызывать функцию.

private void buttonclick(object sender, EventArgs e)
{
IEnumerable<int> range = Enumerable.Range(1,10000);

var qry = TestSlowLoadingEnumerable(range);

//We begin the call and give it our callback delegate
//and a delegate to an error handler
AsynchronousQueryExecutor.Call(qry, HandleResults, HandleError, OnFirstItem);
}

Если это соответствует вашим ожиданиям, вы можете использовать это, чтобы начать свою работу с обработанного первого элемента.

ответил(а) 2015-01-20T14:56:00+03:00 5 лет, 5 месяцев назад
-6

Это довольно просто с TPL.

Здесь фиктивный "медленный" счетчик, который должен выполнять небольшую работу между получением предметов:

static IEnumerable<int> SlowEnumerator()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
yield return i;
}
}

Здесь фиктивный бит работы с каждым элементом в последовательности:

private static void DoWork(int i)
{
Thread.Sleep(1000);
Console.WriteLine("{0} at {1}", i, DateTime.Now);
}

И вот как вы можете одновременно запускать "бит работы" по одному элементу, который возвращал счетчик, и спросить счетчик для следующего элемента:

foreach (var i in SlowEnumerator())
{
Task.Run(() => DoWork(i));
}

Вы должны выполнять работу каждую секунду - не каждые 2 секунды, как вы ожидали бы, если бы вам пришлось чередовать два вида работы:

0 at 20/01/2015 10:56:52
1 at 20/01/2015 10:56:53
2 at 20/01/2015 10:56:54
3 at 20/01/2015 10:56:55
4 at 20/01/2015 10:56:56
5 at 20/01/2015 10:56:57
6 at 20/01/2015 10:56:58
7 at 20/01/2015 10:56:59
8 at 20/01/2015 10:57:00
9 at 20/01/2015 10:57:01

ответил(а) 2015-01-20T13:57:00+03:00 5 лет, 5 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема