lexeme
lexeme

Reputation: 2973

TPL-based looping service: right worker method signature, asynchrony

Regarding the right worker method signature I need to understand the following:

Note

Despite the cpu-bound code, there's a choice to call async versions of io-bound methods (sql queries). So it may be all sync or partially async. As for the nature of code in the Worker method.


public class LoopingService
{
    private CancellationTokenSource cts;
    // ..

    void Worker(CancellationToken cancellationToken)
    {
        while(!cancellationToken.IsCancellationRequested)
        {
            // mixed, CPU/IO-bound code

            try {
                // sql query (can be called either as sync/async)
                var lastId = documentService.GetLastDocument().Id;

                // get next document from a public resource (third-party code, sync)
                // can be moved to a web api
                var document = thirdPartyDocumentService.GetNextDocument(lastId);

                // apply different processors in parallel
                var tasksList = new List<Task>();

                foreach(var processor in documentService.Processors) {
                    // each processor checks if it's applicable
                    // which may include xml-parsing, additional db calls, regexes
                    // if it's applicable then document data is inserted into the db
                    var task = new Task(() => processor.Process(document));
                    tasksList.Add(task);
                    task.Start();
                }

                // or 
                // var tasksList = documentService.ProcessParallel(document);

                Task.WaitAll(tasksList.ToArray(), cancellationToken);
            }
            catch(Exception ex) {
                logger.log(ex);
            }
        }  
    }

    public void Start()
    {
        this.cts = new CancellationTokenSource();
        Task.Run(() => this.Worker(cts.Token));
    }

    public void Stop()
    {
        this.cts.Cancel();
        this.cts.Dispose();
    }

}

Upvotes: 1

Views: 56

Answers (1)

mm8
mm8

Reputation: 169330

is there a point in returning Task instead of void for Worker method?

If Worker is a truly asynchronous method it should return a Task for you to be able to await it. If it's just a synchronous method runnning on a background thread there is no point of changing the return type from void provided that the method is not supposed to return anything.

what should be the return value of Worker method when marked as returning Task object?

Nothing. Provided that the method is asynchronous and marked as async with a return type of Task, it shouldn't return any value:

async Task Worker(CancellationToken cancellationToken) { ... }

Note that there is no point of defining the method as async unless you actually use the await keyword in it.

what signature and body of Worker method should be given the work it completes is long-running CPU/IO-bound work? Should I follow this recommendation?

Yes, probably. If you for some reason are doing both asynchronous and synchronous (CPU-bound) work in the same method, you should prefer to using an asynchronous signature but not wrap the synchronous stuff in Task.Run. Then your service would look something like this:

public class LoopingService
{
    private CancellationTokenSource cts;

    async Task Worker(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await ...
        }
    }

    public async Task Start()
    {
        this.cts = new CancellationTokenSource();
        await this.Worker(cts.Token).ConfigureAwait(false);
    }

    public void Stop()
    {
        this.cts.Cancel();
        this.cts.Dispose();
    }

}

Ideally your method should be either asynchronous or CPU-bound but not both though.

Upvotes: 3

Related Questions