Запустить метод параллельно, где метод запуска не может быть переопределен

#c# #.net

#c# #.net

Вопрос:

Я новичок в C #. У меня есть метод в базовом классе, который запускает другой метод, который может быть переопределен

 public abstract class BaseClass
{
  protected SomeResults processAllItems()
   {
      //code process All Items one by one
      for(Item item : allItems) 
          {  processItem(item);  }
   }

   protected abstract void processItem(Item item);

} 


class MyInheritingClass : BaseClass
{
    protected override void processItem(Item item)
    {
       // do my task
    }
}

  

что я хочу сделать, так это запустить метод ProcessItem() параллельно. Однако я не могу переопределить метод запуска processAllItems()Я могу сделать что-то вроде

 class MyInheritingClass : BaseClass
{
    protected override void processItem(Item item)
    {
       Task.run(() => runTask(item) )
    }

    protected override void runTask(Item item )
    {
       // do my task here
    }

}

  

Но у меня есть следующие проблемы

  1. ProcessItem просто поставит все элементы в очередь, а processAllItems предположит, что все элементы обрабатываются, пока они все еще выполняются асинхронно
  2. Если я использую task.подождите, он вернется к той же проблеме, когда элементы будут обрабатываться один за другим, а не параллельно. Может кто-нибудь помочь мне с тем, как я могу запустить ProcessItem параллельно и гарантировать, что processAllItems завершится только тогда, когда все элементы будут обработаны.
  3. могу ли я переопределить processAllItems()?

Комментарии:

1. Можете ли вы изменить реализацию processAllItems() в своем базовом классе?

2. определить переопределение? вы имеете в виду, что вы не можете изменить реализацию?

3. Нет. Это часто используемый класс, смогу ли я переопределить защищенные SomeResults processAllItems()??

4. под переопределением я подразумеваю изменение реализации processAllItems, где я мог бы изменить способ запуска ProcessItem

5. Почему бы не сделать processAllItems() virtual, чтобы вы могли его переопределить?: protected virtual SomeResults processAllItems()

Ответ №1:

Самый простой способ — сделать processAllItems() виртуальный. В этом случае вы можете переопределить его и изменить его поведение в вашем подклассе.

Если в вашем случае это невозможно (например, потому BaseClass , что является частью внешней библиотеки), вы можете использовать new ключевое слово в вашем подклассе:

 public class MyInheritingClass : BaseClass
{
  protected new SomeResults processAllItems()
   {
      Parallel.ForEach(allItems, processItems);
   }
} 
  

Но это имеет серьезный недостаток:

 BaseClass test = new MyInheritingClass();
test.processAllItems(); // will call the version of the base class in process in sequence
MyInheritingClass test2 = new MyInheritingClass();
test2.processAllItems(); // will call the version of the subclass in process in parallel
  

Вы должны проверить, можете ли вы применить new ключевое слово в своей базе кода.

Совершенно другое решение, которое может вам помочь: знаете ли вы, какой последний элемент allItems ? Тогда вы можете сделать что-то вроде этого:

 class MyInheritingClass : BaseClass
{
    private List<Task> tasks = new List<Task>();

    protected override void processItem(Item item)
    {
       tasks.Add(Task.run(() => runTask(item) ));
       if(item == lastItem)
       {
          Task.WaitAll(tasks.ToArray());
          tasks.Clear();
       }
    }

    protected override void runTask(Item item )
    {
       // do my task here
    }
}
  

Комментарии:

1. На самом деле, это не будет работать так, как написано выше: каждый экземпляр MyInheritingClass будет иметь свой собственный список задач, добавляя в него только свою собственную задачу ProcessItem. Только ‘lastitem будет ждать завершения… Вы можете изменить это поведение, сделав «задачи» статическими и используя отложенное создание экземпляра в конструкторе…