У меня серьезные проблемы с производительностью, т.е. четыре ядра работают на 100%, блокируя все остальное, когда я хочу показать миниатюры в сетке, содержащей более 10 тыс. Записей.
При отображении строки возникает событиепользователю, который загружает изображение.В этом случае экземпляр GridImageManager
вызывается с AddAndStartLoading
.Когда я прокручиваю вниз, это событие запускается слишком часто, и мои ядра заполнены потоками.
Я пытался переписать код, используя Callbacks и список еще для загрузки ячеек, но этот список не является потокобезопасным, что приводит к исключениям, когда я хочу удалить элемент, который уже был удален из другогоthread.
Затем я попытался использовать семафоры, но с ними я не могу реализовать WaitForLoadingCompleted
-метод, потому что не могу получить доступ к количеству свободных ресурсов.
Я также прочитал вЗадачи, но не могли понять, как они мне помогут.
Мои ограничения следующие:
- Загрузка изображений должна начаться сразу же после первого вызова
AddAndStartLoading
. - В любой момент должно выполняться не более
N
процессов загрузки. - Последующие вызовы
AddAndStartLoading
могут ждать, но должны быть обработаны, как только количество процессов загрузки упадет ниже N
. WaitForLoadingCompleted
должен дождаться окончания загрузки всех потоков, в то время как, с другой стороны, разрешено делать Application.DoEvents()
.Я не могу рефакторировать это в данный момент.
Какие шаблоны / стратегии я должен использовать для достижения этой цели?
Чтобы дать вам идею: это моя первая попытка.Это работает до точки, где CellsToLoad
становится пустым.
public class GridImageManager
{
public GridImageManager()
{
CellsToLoad = new List<(UltraGridCell cell, ImageLoader loader)>();
LoadingCells = new Dictionary<UltraGridCell, ImageLoader>();
}
public delegate void ImageLoader(object obj);
private List<(UltraGridCell cell, ImageLoader loader)> CellsToLoad { get; set; }
private Dictionary<UltraGridCell, ImageLoader> LoadingCells { get; set; }
public void AddCellToLoad(UltraGridCell cell, ImageLoader loader)
{
CellsToLoad.Add((cell, loader));
StartLoading();
}
private void CallbackImageLoaded(IAsyncResult ar)
{
if (ar.AsyncState is UltraGridCell cell)
{
LoadingCells.Remove(cell);
StartLoading();
}
}
private void StartLoading()
{
if (LoadingCells.Count >= 10)
return;
if (CellsToLoad.Count == 0)
return;
var next = CellsToLoad.First();
CellsToLoad.Remove(next);
LoadingCells.Add(next.cell, next.loader);
next.loader.BeginInvoke(null, CallbackImageLoaded, next.cell);
}
public void WaitForLoadingCompleted()
{
while (CellsToLoad.Count > 0 && LoadingCells.Count > 0)
{
Application.DoEvents(); // Can't refactor this right now
}
}
}