Я столкнулся с некоторыми проблемами, пытаясь оптимизировать старый код.Общая картина такова: существует «механизм экспорта», который раскручивает некоторый объект «записи» в зависимости от желаемого результата.Запись раскручивает объект DataReader
и подписывается на его события, чтобы он мог обрабатывать считываемые данные.Затем он запускает длительный метод «GetData» в читателе.Это извлекает данные из устаревшей базы данных, что занимает много времени (!).Устройство чтения данных обрабатывает возвращенные значения и запускает несколько событий, позволяющих записывающему устройству обрабатывать данные.
Ниже приведен очень упрощенный пример псевдокода DataReader.
class DataReader
{
// delegates
internal delegate void DataRowReadHandler(object sender, DataRowReadArgs e);
internal delegate void DataProgressChangedHandler(object sender, DataProgressChangedArgs e);
internal delegate void DataReadCompleteHandler(object sender, DataReadCompleteArgs e);
// events
internal event DataProgressChangedHandler DataProgressChanged;
internal event DataReadCompleteHandler DataReadCompleted;
internal event DataRowReadHandler DataRowRead;
// this methods chomps on and on and raises an event when the database read returns something
internal void GetData()
{
for (int totalrows = 0; totalrows < _cursor.RowCount; totalrows += _maxrows)
{
// I want to keep GetRawData running while the data it fetched is being processed
string[][] rawdata = _cursor.GetRawData(_maxrows);
// -- a ton of post-processing I want to do while database is being read--
// and then report progress
foreach (row in rawdata)
{
DataRowReadArgs args = new DataRowReadArgs(row.Index)
OnDataRowRead(args); // raise event after each row
}
DataProgressChangedArgs args = new DataProgressChangedArgs(batch, counter);
OnDataProgressChanged(args); // raise event after each batch of rows
}
// report we're done
DataReadCompleteArgs e = new DataReadCompleteArgs(counter);
OnDataReadCompleted(e); // done with reading data
}
protected virtual void OnDataProgressChanged(DataProgressChangedArgs e)
{
DataProgressChangedHandler handler = DataProgressChanged;
if (handler != null)
handler(this, e);
}
protected virtual void OnDataReadCompleted(DataReadCompleteArgs e)
{
DataReadCompleteHandler handler = DataReadCompleted;
if (handler != null)
handler(this, e);
}
protected virtual void OnDataRowRead(DataRowReadArgs e)
{
DataRowReadHandler handler = DataRowReadRead;
if (handler != null)
handler(this, e);
}
}
ЧтоЯ хочу: поддерживать чтение базы данных (которое будет самым медленным) и обрабатывать возвращаемые данные всякий раз, когда результат запроса становится доступным.То есть: постобработка данных в считывателе, запуск событий, и обработчики в обработчиках обрабатывают их , пока чтение базы данных продолжается .В идеале я также хочу, чтобы какой-нибудь токен отмены останавливал чтение, когда что-то идет не так, но обо всем по порядку.Я НЕ хочу касаться системы, основанной на событиях, на которую полагаются многие классы, я только хочу, чтобы чтение базы данных выполнялось параллельно и чтобы остальная часть кода отвечала всякий раз, когда есть результат.
Iбаловался с await / async и TaskCompletionSource и еще чем-то еще вот уже неделю, но, похоже, все еще не в состоянии обдумать это.Я подошел близко, мне действительно удалось составить список задач, передать его в промежуточный метод, который будет обрабатывать каждую задачу по мере ее завершения, и ждать этого.
internal async Task GetDataAsync()
{
IList<Task<string[][]>> tasks = CreateCursorReadTasks();
var processingTasks = tasks.Select(AwaitAndProcessAsync).ToList();
await Task.WhenAll(processingTasks);
// this isn't 'awaited' in the sense I expected
// also, what order are they performed in? The database is single-threaded, no queues, nothing
// I need to fire my 'done' event only after all tasks have finished
}
private IList<Task<string[][]>> CreateCursorReadTasks()
{
IList<Task<string[][]>> retval = new List<Task<string[][]>>();
for (int totalrows = 0; totalrows < this._cursor.RowCount; totalrows += _maxrows)
{
retval.Add(Task.Run(() => _cursor.GetRawData(_maxrows)));
}
return retval;
}
internal async Task AwaitAndProcessAsync(Task<string[][]> task)
{
string[][] rawdata = await task;
// Do all the post-processing and fire the events like in the GetData method of DataReader
}
Помимо всего этого, кажущегося чрезмерно сложным, я сталкиваюсь с двумя проблемами: а) все мои обработчики событий кажутся пустыми, даже если я подписан на них, и б) я не знаю, где и как повышатьзавершенное событие.
Мой вопрос: когда вы посмотрите на мой метод GetData
в классе DataReader
, как бы вы предложили мне сделать так, чтобы очень дорогие вызовы базы данных выполнялись асинхронно?