Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Way to creating async wrapper

How better to create async wrapper for synchronous method?

// sync method
public void LongOperation()
{
    //code...
}


// versions of wrapper
public async Task LongOpertionWrapperAsyncV1()
{
    var task = Task.Factory.StartNew(LongOperation);
    await task.ConfigureAwait(false);
}

public Task LongOpertionWrapperAsyncV2()
{
    var task = Task.Factory.StartNew(LongOperation);
    task.ConfigureAwait(false);
    return task;
}

Although the use of both versions don't differ.

async Task Executor()
{
    await LongOpertionWrapperAsyncV1();
    await LongOpertionWrapperAsyncV2();
}

For methods that return value (Task<T>), I use the first version.

But I would like to know your opinion.

And there is a general difference between these versions?

like image 422
iwedaz Avatar asked Nov 03 '14 15:11

iwedaz


Video Answer


2 Answers

Neither solution is correct. The best answer is to not expose asynchronous methods for synchronous code. I go more into the "why" on my blog.

If your code is asynchronous, then use async and await. If it's not, then don't. It should be up to the caller how they want to invoke the code (e.g., using Task.Run).

like image 191
Stephen Cleary Avatar answered Sep 28 '22 20:09

Stephen Cleary


You should use "something like" V2:

public Task LongOpertionWrapperAsyncV2()
{
    return Task.Run(LongOperation);
}

and

async Task Executor()
{
    await LongOpertionWrapperAsyncV2().ConfigureAwait(false);
}

This saves you one context-switch compared to V1. As long as you do not need to "await another operation" and the async-Task is the last operation in the method you can just return the task instead of awaiting it and leave the await to the caller (which also can or can not add ConfigureAwait).

Task.Factory.StartNew is only necessary if you want to supply TaskCreationOptions.LongRunning like HPT suggested.

UPDATE:
As Stephen already meantioned: There are VERY few cases where you should do async over sync (but there are). So think about what exectly you are doing before implementing something like this. Simplified said: If it is CPU-bound work DON'T do it, if it is some kind of "waiting for IO" MAYBE do it.
We have a case here where we developed a "nearly async all the way" library, which is to control different HW-devices. There the whole "generic library" is asynchronous, but some of the low-level device drivers and/or access libraries do not support async so on the very lowest level we do something like:

public Task<byte[]> ReadAsync(int length)
{
    return Task.Run(() => hwDevice.Read(length));
}

The hwDevice.Read does still lock the thread, but not the CPU, so the UI is responsive during that time we are waiting for the IO (in "real live" the is also some cancellation and error handling logic around it).

like image 23
Christoph Fink Avatar answered Sep 28 '22 18:09

Christoph Fink