Should I expose asynchronous wrappers for synchronous methods?

Source: Internet
Author: User
Tags wrappers

Lately I ' ve received several questions along the lines of the following, which I typically summarize as "Async over Sync":

In my library, I has a method "public T Foo ();". I ' m considering exposing an asynchronous method this would simply wrap the synchronous one, e.g. "Public task<t> Foo  Async () {return Task.run (() = Foo ());} ". Is this something you ' d recommend I does in my library?

My short answer to such a question is "No."  But this doesn ' t make for a very good blog post. So here's my longer, more reasoned answer ...

Why asynchrony?

There is primary benefits I see to asynchrony:scalability and offloading (e.g. responsiveness, parallelism).  Which of these benefits matters to you are typically dictated by the kind of application ' re writing. Most client apps care about Asynchrony for offloading reasons, such as maintaining responsiveness of the UI thread, though There is certainly cases where scalability matters to a client as well (often on more technical computing/agent-based  Simulation workloads). Most servers apps care about Asynchrony for scalability reasons, though there is cases where offloading matters, such as I n Achieving parallelism in Back-end compute servers.

Scalability

The ability to invoke a synchronous method asynchronously does nothing for scalability, because you ' re typically still con Suming the same amount of resources you would has if you ' d invoked it synchronously (in fact, you ' re using a bit more, si nCE there ' s overhead incurred to scheduling something), you ' re just using different resources to do it, e.g. a thread fro  m a thread pool instead of the specific thread were executing on. The scalability benefits touted for asynchronous implementations is achieved by decreasing the amount of resources you us E, and that needs to is baked into the implementation of a asynchronous method ... it's not something achieved by wrapping a Round it.

As an example, consider a synchronous method Sleep this doesn ' t return for N milliseconds:

public void Sleep (int millisecondstimeout)
{
Thread.Sleep (millisecondstimeout);
}

Now, consider the need to create a asynchronous version of this, such that the returned Task doesn ' t complete for N Milli  Seconds. Here's one possible implementation, simply wrapping Sleep with Task.run to create a sleepasync:

Public Task sleepasync (int millisecondstimeout)
{
Return Task.run (() = Sleep (millisecondstimeout));
}

And here's another that doesn ' t use Sleep, instead rewriting the implementation to consume fewer resources:

Public Task sleepasync (int millisecondstimeout)
{
Taskcompletionsource<bool> TCS = null;
var t = new Timer (Delegate {TCS. Trysetresult (TRUE); }, Null,–1,-1);
TCS = new taskcompletionsource<bool> (t);
T.change (millisecondstimeout,-1);
Return TCS. Task;
}

Both of these implementations provide the same basic behavior, Both completing the returned task after the timeout has exp  Ired.  However, from a scalability perspective, the latter was much more scalable. The former implementation consumes a thread from the thread pool for the duration of the wait time, whereas the latter sim Ply relies on a efficient timer to signal the Task when the duration have expired.

offloading

The ability to invoke a synchronous method asynchronously can is very useful for responsiveness, as it allows your to of Fload long-running operations to a different thread.  this isn ' t on How many resources you consume, but R Ather is about which resources you consume.  for example, in a UI app, the specific thread handling pumping U I messages is ' more valuable ' for the user experience than be other threads, such as those on the threadpool.  so, a Synchronously offloading the invocation of a method from the UI thread to a ThreadPool thread allows us to use the less VA Luable resources.  This kind of offloading does is require modification to the implementation of the operation being Offloaded, such that the responsiveness benefits can be achieved via wrapping.

The ability to invoke a synchronous method asynchronously can also is very useful not just for changing threads, but MO Re generally for escaping the current context.  for example, sometimes we need to invoke some user-provided code but We ' re not in a good place to does it (or we ' re not sure if we be) .  maybe a lock is held higher up the stack and we do N ' t want to invoke the user code while holding the lock.  maybe we suspect we ' re being invoked by some user code that doesn ' t expect us to take a very long time. Rather than invoking the operation synchronously and as part of whatever are higher-up on the call stack, we can invoke the Functionality asynchronously.

The ability to invoke a synchronous method asynchronously are also important for parallelism. Parallel programming is all on taking a single problem and splitting it up into sub-problems so can each be processed  concurrently. If you were-to-split a problem into sub-problems and then process all Sub-problem serially, you wouldn ' t get any parallel  ISM, as the entire problem would is processed on a single thread. If, instead, offload a sub-problem to another thread via asynchronous invocation, you can then process the Sub-problem  s concurrently. As with responsiveness, this kind of offloading does not require modification to the implementation of the operation being offloaded, such that parallelism benefits can be achieved via wrapping.

What is the does this and does with my question?

Let's get back to the core question:should we expose an asynchronous entry point for a method that ' s actually synchronous  ? The stance we ' ve taken in. NET 4.5 and the task-based Async Pattern is a staunch "no."

Note that in my previous discussion of scalability and ofloading, I called out that the the-the-same-to achieve scalability benefit S is by modifying the actual implementation, whereas offloading can being achieved by wrapping and doesn ' t require modifying  The actual implementation.  That ' s the key.  Wrapping a synchronous method with a simple asynchronous fa?ade does no yield any scalability benefits. And in such cases, by exposing-the-synchronous method, you get some nice benefits, e.g.

    • Surface area of the your library is reduced.  This means less cost to you (development, testing, maintenance, documentation, etc).  It also means that your user ' s choices is simplified.  While some choice are typically a good thing, too much choice often leads to lost productivity. If I as a user am constantly faced with both a synchronous and an asynchronous method for the same operation, I constantly Need to evaluate which of the pairs are the right one for me and use in each situation.
  • Your users would know whether there is actually scalability benefits to using exposed asynchronous APIs, since by Definiti On and only APIs that benefit scalability is exposed asynchronously.
  • the choice of whether to invoke the synchronous method, asynchronously is, and the developer. Async wrappers around sync methods has overhead (e.g. allocating the object to represent the operation, context switches, Synchronization around queues, etc.).   If, for example, your customer is writing a high-throughput server app, they don ' t want to spend cycles on overhead That's not actually benefiting them on any it, so they can just invoke the synchronous method.  If both the synchro Nous method and an asynchronous wrapper around it was exposed, the developer is then faced with thinking they should invok E The asynchronous version for scalability reasons, but reality would actually be hurting their throughput by paying for The additional offloading overhead without the scalability benefits.

If A developer needs to achieve better scalability, they can use any async APIs exposed, and they don ' t has to pay add Itional overhead for invoking a faux async api.  If A developer needs to achieve responsiveness or parallelism with S Ynchronous APIs, they can simply wrap the invocation with a method like Task.run.

The idea of exposing ' async over sync ' wrappers is also a very slippery slope, which taken to the extreme could result In every single method being exposed in both synchronous and asynchronous forms.  Many of the folks this ask me about This practice was considering exposing async wrappers for long-running Cpu-bound operations.  The intention is a goo D One:help with responsiveness.  and as called out, responsiveness can easily is achieved by the consumer of the API , and the consumer can actually do in the right level of chunkiness, rather than for each chatty individual operation.& nbsp Further, defining what operations could being long-running is surprisingly difficult.  the time complexity of many metho DS often varies significantly.

Consider, for example, a simple method like dictionary<tkey,tvalue>. ADD (Tkey,tvalue) .  This was a really fast method, right?  Typically, yes, but remember how dictionary works:it N Eeds to hash the key in order to find the right bucket to put it into, and it needs to check for equality of the key with Other entries already in the bucket.  those hashing and equality checks can result in calls to user code, and who Kno WS What those operations does or how long they take.  should every method on dictionary has an asynchronous wrapper ex Posed? That's obviously an extreme example, but there is simpler ones, like regex.  the complexity of the regular Expressio n pattern provided to Regex as well as the nature and size of the input string can has significant impact on the running Time of matching with regex, so much so this regex now supports optional timeouts ... should every method on Regex has an as Ynchronous equivalent?  I really hope not.

guideline

This have all been a very long-winded is saying that I believe the only asynchronous methods that should be exposed is  Those that has scalability benefits over their synchronous counterparts. Asynchronous methods should not being exposed purely for the purpose of Offloading:such benefits can easily being achieved by T He consumer of synchronous methods using functionality specifically geared towards working with synchronous methods Asynch ronously, e.g Task.run.

Of course, there is exceptions to this, and you can witness a few such exceptions in. NET 4.5.

For example, the abstract base stream type provides Readasync and WriteAsync methods.  in most cases, derived stream Implementations work with data sources, aren ' t in-memory, and thus involve disk I/O or network I/O of some kind.  As such, it ' s very likely that derived implementations would be able to provide implementations of Readasync and Writeasyn C that utilize asynchronous I/O rather than synchronous I/O that blocks threads, and thus there is scalability benefits T O have Readasync and WriteAsync methods.  Further, we want to is able to work with these methods polymorphically, W Ithout regard for the concrete stream type, so we want to has these as virtual methods on the base class.  however, The base class doesn ' t know how to implement these base implementations with asynchronous I/O, so the best it can does is PR Ovide asynchronous wrappers for the synchronous Read and Write methods (in actuality, Readasync and WriteAsync actually WR AP Beginread/endread and Beginwrite/endwrite, respectively, which if not overridden would in turn wrap the synchronous Read and Write methods with An equivalent of Task.run).

Another example in the same vein are TextReader, providing methods like Readtoendasync, which on the base class simply uses  A Task to wrap an invocation of textreader.readtoend. The expectation, however, is and the derived types developers actually use would override Readtoendasync to provide Implem Entations that benefit scalability, such as StreamReader ' s Readtoendasync method which utilizes stream.readasync.

Should I expose asynchronous wrappers for synchronous methods?

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.