Запустите асинхронную задачу, не дожидаясь ее завершения, но проверьте ее выполнение - PullRequest
0 голосов
/ 15 февраля 2019

Я очень тяжело искал правильный ответ, и все, что я нахожу, слишком сложно или не выполняет то, что я ищу.Случай прост:

  1. Я хочу запускать задачу асинхронно время от времени.Одна и та же задача всегда.Нет определенного интервала времени для его запуска (предположим, что он случайный).
  2. Задача не принимает аргументов и ничего не возвращает.
  3. Я не хочу ждать, пока она завершится,Это не может помешать работе остальной программы.
  4. Я хочу проверить, завершено ли это, прежде чем запускать его снова.Не хочу, чтобы одна и та же задача выполнялась много раз одновременно.Просто по одному.
  5. Я не хочу использовать таймеры или глобальные переменные, такие как семафоры или что-то еще.Просто простое и ясное решение для очень простой проблемы.

Я пробовал фоновые рабочие, но задачи перекрываются, так как я не могу найти надежный способ проверки выполнения.Я пытался запустить задачи, но они не могут быть перезапущены.Я пытался с async / await, но я не хочу ждать завершения.

РЕДАКТИРОВАТЬ: Я предоставлю дополнительную информацию.Это приложение для распознавания лиц SW.Я должен обрабатывать 3 камеры, и я использую EmguCV.Каждая камера подключается к событию ImageGrabbed, которое называется «ProcessFrame», поэтому у меня есть ProcessFrame1, ProcessFrame2 и ProcessFrame3.События запускаются почти со скоростью fps каждой камеры, учитывая, что частота очень высока.В каждом событии я делаю снимок и показываю его в блоке изображений (в картинке Эмгу).Каждые 5 снимков я проверяю, есть ли у меня хотя бы один снимок с каждой камеры, и в этом случае вместо того, чтобы показывать его в графическом окне, я выполняю распознавание лиц на каждом изображении.Это задача, которую я хочу выполнить в отдельной задаче, чтобы избежать остановки живого видео для каждой камеры.

Сейчас я пытаюсь использовать семафор, как предлагали некоторые из вас, хотя у меня были некоторыепроблема в настройке возможности выполнения DetectFace () в трех событиях, поэтому я просто оставил один.Вот фрагмент кода:

public Form1()
{
    InitializeComponent();

    //Instantiate each camera
    //Subscribe to ProcessFrame1, ProcessFrame2 and ProcessFrame3
}

private void ProcessFrame1(object sender, EventArgs e)
{
    if (captures[0] != null)  //captures[0] is the handle for the camera 1
    {
        Mat snapshot = new Mat();
        captures[0].Retrieve(snapshot);

        if (snapshot != null)
        {
            frameCounter1++;
            if (frameCounter1 > 5 && taskCompleted)
            {
                frameCounter1 = 0;

                if (images[0] == null)
                {
                    Image<Bgr, Byte> img = snapshot.ToImage<Bgr, Byte>();
                    images[0] = img.ToBitmap();
                }

                if (images[0] != null && images[1] != null && images[2] != null)
                {
                    Thread hilo = new Thread(() => DetectFace());
                    hilo.IsBackground = true;
                    hilo.Start();
                }

                return;
            }
            else
                imageBox1.Image = snapshot;
        }
    }
}

private void ProcessFrame2(object sender, EventArgs e)
{
    if (captures[1] != null)  //captures[1] is the handle for the camera 2
    {
        Mat snapshot = new Mat();
        captures[1].Retrieve(snapshot);

        if (snapshot != null)
        {
            frameCounter2++;
            if (frameCounter2 > 5 && taskCompleted)
            {
                frameCounter2 = 0;

                if (images[1] == null)
                {
                    Image<Bgr, Byte> img = snapshot.ToImage<Bgr, Byte>();
                    images[1] = img.ToBitmap();
                }

                //I used to have the checking to fire up another DetectFace here

                return;
            }
            else
                imageBox2.Image = snapshot;
        }
    }
}

private void ProcessFrame3(object sender, EventArgs e) //Same as ProcessFrame2

private void DetectFace()
{
    taskCompleted = false;

    //Processing of Images
    //Clear array of images

    taskCompleted = true;
}

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Я наконец достиг многопоточного решения, используя BlockingCollection.Спасибо @Damien_The_Unbeliever за указание в правильном направлении.

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

Как я уже писал в этом вопросе, здесь я должен сделать снимки с X разных веб-камер, показать их в X разных боксах изображений и, как быстроКак я могу (не прерывая частоту кадров показанного видео), выполнить обработку изображения вместо показа кадра в imageBox.При использовании BlockingCollection мне не нужно фиксировать частоту для обработки кадров, как я делал раньше (каждые 5 кадров).Теперь я могу показать кадр каждой камеры, если у меня уже есть кадр из этой камеры, добавленный в BlockingCollection.

Еще одна важная деталь, на которую следует обратить внимание, в документации .NET для BlockingCollection говорится, что по умолчаниюон реализует FIFO, так как это ConcurrentQueue на нижнем уровне, но я думаю, что это не так, поскольку мне пришлось самому определять его при создании экземпляра:

BlockingCollection<Tuple<int, Image>> tupleCollection = new BlockingCollection<Tuple<int, Image>>(new ConcurrentQueue<Tuple<int, Image>>(), X);

Как метод Take () может 'Для нацеливания на нужный элемент в коллекции мне пришлось использовать кортеж, чтобы узнать, к какой камере принадлежит кадр, и взять кадры, чтобы определить ConcurrentQueue.

Так что в основном псевдокод это:

void Main()
{
   //Instantiate cameras
   //Subscribe to the ImageGrabbed events of each (producers)

   // A simple blocking consumer with no cancellation.
   Task.Run(() => DetectFace());
}

producer1(sender, e)
{
   //Get snapshot
...
   if (!tupleCollection.Any(x => x.Item1 == 1))
   {
      tupleCollection.Add(new Tuple<int, Image>(1, snapshot));
   }
   else
      imageBox1.Image = snapshot;
}

producer2(sender, e)
{
   //Get snapshot
...
   if (!tupleCollection.Any(x => x.Item1 == 2))
   {
      tupleCollection.Add(new Tuple<int, Image>(2, snapshot));
   }
   else
      imageBox2.Image = snapshot;
}
...
producerX(sender, e)
{
   //Get snapshot
...
   if (!tupleCollection.Any(x => x.Item1 == X))
   {
      tupleCollection.Add(new Tuple<int, Image>(X, snapshot));
   }
   else
      imageBoxX.Image = snapshot;
}

private void DetectFace()
{
   while (true)
   {
      Tuple<int, Image> data = null;
      try
      {
          data = tupleCollection.Take();
      }
      catch (InvalidOperationException) { }

      if (data != null)
      {
         //image processing
      }
   }
}

В большинстве примеров, которые я обнаружил, для прекращения добавления и использования используются условия IsCompletedAdded и IsCompleted, но мне нужно, чтобы оно выполнялось вечно, поэтому оператор while (true).

Я использовал этот код 24/7 в течение последней недели, никаких ошибок в гонках на данный момент, и процессор сильно ограничен,поэтому я очень доволен этим решением и считаю, что оно правильное.

0 голосов
/ 15 февраля 2019

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

Вот документация .NET для чтения по различным состояниям задачи:

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.status?view=netframework-4.7.2

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskstatus?view=netframework-4.7.2

к сожалению, я 'Я не могу дать вам пример кода на данный момент, но я надеюсь, что идея поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...