How to queue up delegates to be executed in series in the background with C#?

前端 未结 2 1569
别跟我提以往
别跟我提以往 2021-01-28 00:51

From a game loop i want to start work in the background that should be executed one after another but should not block the game loop.

So ideally a class Background

2条回答
  •  借酒劲吻你
    2021-01-28 01:38

    One solution is from the excellent Threading in C# E-book. In their section on basic structures, the author makes almost exactly what you're looking for in an example.

    Follow that link and scroll down to Producer/consumer queue.

    In a later section, he addresses that while ConcurrentQueue would work fine too, it performs worse in all cases EXCEPT in highly-concurrent scenarios. But for your low-load case, it may be better just to get something working easily. I don't have personal experience with the claim from the document, but it's there for you to evaluate.

    I hope this helps.

    Edit2: upon Evk's suggestion (thanks!), the BlockingCollection class looks like what you want. By default it uses a ConcurrentQueue under the hood. I particularly like the CompleteAdding method, as well as the ability to use CancellationTokens with it. "Shutdown" scenarios are not always correctly accounted for when things are blocking, but this does it right IMO.

    Edit 3: As requested, a sample of how this would work with a BlockingCollection. I used the foreach and GetConsumingEnumerable to make this even more compact for the consumer side of the problem:

    using System.Collections.Concurrent;
    private static void testMethod()
    {
      BlockingCollection myActionQueue = new BlockingCollection();
      var consumer = Task.Run(() =>
      {
        foreach(var item in myActionQueue.GetConsumingEnumerable())
        {
          item(); // Run the task
        }// Exits when the BlockingCollection is marked for no more actions
      });
    
      // Add some tasks
      for(int i = 0; i < 10; ++i)
      {
        int captured = i; // Imporant to copy this value or else
        myActionQueue.Add(() =>
        {
          Console.WriteLine("Action number " + captured + " executing.");
          Thread.Sleep(100);  // Busy work
          Console.WriteLine("Completed.");
        });
        Console.WriteLine("Added job number " + i);
        Thread.Sleep(50);
      }
      myActionQueue.CompleteAdding();
      Console.WriteLine("Completed adding tasks.  Waiting for consumer completion");
    
      consumer.Wait();  // Waits for consumer to finish
      Console.WriteLine("All actions completed.");
    }
    

    I added in the Sleep() calls so that you can see that things are added while other things are being consumed. You can also choose to launch any number of that consumer lambda (just call it an Action, then launch the Action multiple times) or the addition loop. And at any time you can call Count on the collection to get the number of tasks that are NOT running. Presumably if that's non-zero, then your producer Tasks are running.

提交回复
热议问题