-1

I developing many algorithms that did most of the threading by themselves by using regular Threads. The approach was always as following

float[] GetData(int requestedItemIndex)

With the method above and index was pushed into some messages queue that was processed by the thread of the inidividual algorithm. So in the end the interface of the algorithm was like this:

public abstract class AlgorithmBase
{
    private readonly AlgorithmBase Parent;

    private void RequestQueue()
    {
    }

    public float[] GetData(int requestedItemIndex) => Parent.GetData(requestedItemIndex);
}

The example is very primitive, but just to get the clue. The problem is that I can chain algorithms which currently works fine with my solution. As you can see every GetData calls another GetData of a parent algorithm. This can of course get more complex and of course there needs to be a final parent as data source, otherwise I would get StackOverflowExceptions.

Now I try to change this behavior by using async/await. My question here is that if I rewrite my code I would get something like this:

  public abstract class AlgorithmBase
    {
        private readonly AlgorithmBase Parent;


        public async Task<float[]> GetDataAsync(int requestedItemIndex, CancellationToken token = default)
        {
            var data = await Parent.GetDataAsync(requestedItemIndex);

            return await Task.Run<float[]>(async () => ProcessData());
        }            
    }

Now, I have chained the algorithms, any every new algorithm spans another Task, which can be quite time consuming when this is done many times.

So my questions is if there is a way where the next task can be embedded in the already running task, by using the defines interface?

msedi
  • 1,437
  • 15
  • 25
  • 1
    The thing is with tasks, the task scheduler can probably manage them better than you can in both cpu bound and io bound workloads. im not sure what the problem actually is here. If you want to pipeline data and workflows, it might be better to look at DataFlow or RX – TheGeneral Sep 02 '19 at 06:50
  • @TheGeneral: I had a look at both of these solution (DataFlow and Rx). My problem is that all of my requirement are pull-based and not push-based. – msedi Sep 02 '19 at 07:14
  • @GSerg: Thanks for the comment. To be honest, I didn't test the code, I just inserted it for my explanations. – msedi Sep 02 '19 at 07:15
  • The question is incomplete. async/await is useful for async I/O and interfacing with a MessagePump. It is not clear if either indicator applies here. What is ProcessData() typically doing? – H H Sep 02 '19 at 08:36
  • 1
    @GSerg assuming that `ProcessData` is a synchronous method, the example code will produce a warning (because there is an `async` without `await`), but the result of the method will be awaited just fine. [Task.Run()](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run) understands async delegates. – Theodor Zoulias Sep 02 '19 at 09:53
  • 1
    @TheodorZoulias For some reason it crossed in my mind with https://stackoverflow.com/q/24777253/11683. – GSerg Sep 02 '19 at 13:21

2 Answers2

1

There is no need to explicitly use Task.Run. You should avoid that, and leave that choice to the consumer of AlgorithmBase class.

So, you can quite similarly implement async version, in which Task object will be propagated from parents to childred:

public abstract class AlgorithmBase
{
    private readonly AlgorithmBase Parent;

    private void RequestQueue()
    {
    }

    public Task<float[]> GetDataAsync(int requestedItemIndex) 
         => Parent.GetDataAsync(requestedItemIndex);
}

Eventually, some "parent" will implement GetDataAsync, in the same manner as synchronous counterpart.

public class SortAlgorithm : AlgorithmBase
{
    public override async Task<float[]> GetDataAsync(int requestedItemIndex)
    {
        // asynchronously get data 
        var data = await Parent.GetDataAsync(requestedItemIndex);

        // synchronously process data and return from asynchronous method
        return this.ProcessData(data);
    }

    private float[] ProcessData(float[] data)
    {
    }
}

In the end, consumer of SortAlogirthm can decide whether to await it, or just fire-and-forget it.

var algo = new SortAlgorithm();

// asynchronously wait until it's finished
var data = await algo.GetDataAsync(1);

// start processing without waiting for the result
algo.GetDataAsync(1);

// not needed - GetDataAsync already returns Task, Task.Run is not needed in this case
Task.Run(() => algo.GetDataAsync(1));
Darjan Bogdan
  • 3,780
  • 1
  • 22
  • 31
  • 1
    Just try to not think about the threading, but think in term of tasks. Each task will be invoked on an available thread which is taken from the ThreadPool, so threading behind is managed by framework. – Darjan Bogdan Sep 02 '19 at 07:31
  • 1
    If ProcessData() is CPU bound then you have to use a Task.Run() somewhere. – H H Sep 02 '19 at 08:34
0

When awaiting in library code you normally want to avoid capturing and restoring the context each and every time, especially if you are awaiting in a loop. So to improve the performance of your library consider using .ConfigureAwait(false) on all awaits.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104