wpfasync-awaitparallel-processingreal-timeparallel.for

WPF , How to execute an async method many times simultaneously and in same time?


I am writing a WPF project to simulate video display in different structures like buildings.

In this project, I use a special type of binary video with bin extension, in which the colors are stored as red, green and blue.

I have 2 methods, the first one is "ReadDisplayBinFrames", which has 2 tasks of reading bin video and displaying it on the structure. Of course, since these two tasks are asynchronous, I defined the method as async.

public async Task ReadDisplayBinFrames(Product product, bool PlayMode)
    {
        BinFile.SetPlayMode(PlayMode);

        int currentFrameNumber = 0;
        for (int i = 0; BinFile.IsPlayMode == true; i++)
        {
            for (currentFrameNumber = (int)product.LastFrameRead; currentFrameNumber <= product.BinFiles.TotalGame; currentFrameNumber++)
            {
                await Task.Run(() =>
                {
                    product.BinFiles.GetSphereColorFromBin(product.BinFiles.Read(currentFrameNumber), product.Wiring);
                    product.LastFrameRead = currentFrameNumber;
                    Debug.WriteLine($"LastFrameRead {product.LastFrameRead}");


                    product.Wiring.SetSphereColor(product.DelayPlay);
                });

                if (currentFrameNumber >= product.BinFiles.TotalGame)
                {
                    product.LastFrameRead = 0;
                }

                if (animPlayingMode == AnimPlayingMode.SerialAsync)
                {
                    BinFile.SetPlayMode(false);
                }
            }

        }
    }

Since I have a list of structures and I need to be able to display a video on each of them at the same time, I defined a method called "PlayBin".

private async void PlayBin()
    {
        InitBinList();

        for (int i = 0; i < Products.Count; i++)
        {
            if (animPlayingMode == AnimPlayingMode.ParallelSynchronous)
            {
                Parallel.Invoke(async () =>
                {
                    await ReadDisplayBinFrames(Products[i], true);
                    Debug.WriteLine($"LedProducts Count: {Products[i].LastFrameRead} of Product {i}");
                });
            }
            else
            {
                await ReadDisplayBinFrames(Products[i], true);
                Debug.WriteLine($"LedProducts Count: {Products[i].LastFrameRead} of Product {i}");
            }

        }
    }   

When I display the video on one structure, it is displayed without any problem, but when I increase the number of structures (for example, 6), the playback speed decreases slightly compared to the case when there was only one structure, and after a while, the coordination is lost. and each goes forward or backward a few frames.

Videos Of Software Performance


Solution

  • Parallel.Invoke does not support async methods. It expects to execute simple void delegates (e.g. Action<T>). This means, the executed delegates will always run synchronously.

    Your code is a good piece of example why passing an async lambda as argument for an Action introduces a potential bug: because the caller of the delegate expects an Action, it won't await the async method. The async method will return the Task, and the caller will continue immediately (instead of awaiting this Task).

    In your case, Parallel.Invoke will continue before the async methods have run to completion. This will lead to dropping the async context and to unexpected behavior.

    Also note, that an async method must always return a Task or Task<T> (except the method is an event handler).

    Instead of Parallel.Invoke, you should always use Task.WhenAll to execute a number of asynchronous methods and wait for them all to finish:

    // Always let an async method return 'Task' or 'Task<T>' 
    // and await it properly!
    private async Task PlayBinAsync()
    {
      InitBinList();
    
      if (animPlayingMode == AnimPlayingMode.ParallelSynchronous)
      {
        var runningTasks = new List<Task>();
        for (int i = 0; i < Products.Count; i++)
        {
          Task runningTask = ReadDisplayBinFrames(Products[i], true);
          runningTasks.Add(runningTask);
    
          Debug.WriteLine($"LedProducts Count: {Products[i].LastFrameRead} of Product {i}");
        }
    
        await Task.WhenAll(runningTasks);
        return;
      }
    
      for (int i = 0; i < Products.Count; i++)
      {
        await ReadDisplayBinFrames(Products[i], true);
          runningTasks.Add(runningTask);
    
          Debug.WriteLine($"LedProducts Count: {Products[i].LastFrameRead} of Product {i}");
      }
    }