Async is waiting for a separate task at a time

During my interview, I was instructed to create an asynchronous shell over some long running method, process some data, but create it so that only one task could be launched at a time. I was not very familiar with the async/await template, so I did everything possible and wrote some confusion between the task style and the event style, so that my wrapper would execute the task currently being executed and expose the public method and public event, The method took data for processing as an argument, and if the task was not started, started it, if the task was set, it sent data to the queue. The task was to complete the public event upon completion, which sent the results of the process to subscribers and started a new task if there are any registered ones.

So, as you might have guessed up to this point, I was not able to conduct an interview, but now that I have done some research, Iโ€™m trying to figure out how to do it right (it should also have been thread safe, but I was too busy worrying about it ) So my question is: if I have

 public class SynchronousProcessor { public string Process(string arg) { Thread.Sleep(1500); //Work imitation return someRandomString; } } public class AsynchronousWrapper { SynchronousProcessor proc = new SynchronousProcessor(); public async Task<string> ProcessAsync(string arg) { return Task.Run(() => proc.Process(arg)); } } 

or something like this, how to handle calls to ProcessAsync(string) correctly if the task is already running?

+6
multithreading c # asynchronous
source share
3 answers


Many interview questions are asked for purposes other than seeing how you write code. Usually the questions are a little vague to find out what clarifying questions you ask - your questions determine how well you do it. Writing code on the board is secondary at best.

I was tasked with creating an asynchronous shell over some long running method, processing some data

First question: is this long-running method asynchronous? If so, then Task.Run would not be necessary. But if not ...

Follow-up question: if it is not asynchronous, right? Ie, is it I / O-based? If so, then we could take the time to make it asynchronous. But if not ...

The next question is: if we need a shell for the task (around processor-based code or around blocking I / O code), is the environment acceptable for the shell? Ie, is this a desktop / mobile application, not the code that will be used in ASP.NET?

create it to run only one task at a time.

Clarification of questions: if the second request arrives when it is already running, the second request is "queued"? Or is it a "merge" with an existing query? If they are merges, do they need to โ€œdiscardโ€ the input - or some subset of the input?

Each of these questions changes the way the answer is constructed.

Publication of an open method and a public event.

It could be what left him. Between Task<T> / IProgress<T> and Rx events are rarely needed. They really should only be used if you are working in a team that does not recognize Rx.

Oh, and don't worry about the โ€œfailureโ€ of the interview. I have failed more than 2/3 of my interviews during my career. Iโ€™m just talking badly.

+3
source share


As @MickyD already said, you need to know "Best Practices in Asynchronous Programming" to properly solve such problems. Your solution has a code smell because it provides an asynchronous shell with Task.Run for synchronous code . Since you were asked about the development of the library, this will greatly affect your library users.

You should understand that asynchronous not multithreading , as this can be done with a single thread. I like to wait for mail - you do not hire a worker to wait for a mailbox .

Other solutions here are not asynchronous because they violate another rule for async code: they do not block the async action , so you should avoid the lock construct.

So, back to your problem: if you are faced with a task that states

only one task can be started at a time

This is not about lock ( Monitor ), it's about Semaphore(Slim) . If for some reason in the future you will need to improve your code, you can perform several tasks at the same time, you will have to rewrite the code. In the case of using Semaphore you will only need to change one constant. It also has async wrappers for wait methods

Thus, your code may be like this (note that Task.Run has been deleted, as the client is responsible for making it available):

 public class AsynchronousWrapper { private static SemaphoreSlim _mutex = new SemaphoreSlim(1); public async Task<T> ProcessAsync<T>(Task<T> arg) { await _mutex.WaitAsync().ConfigureAwait(false); try { return await arg; } finally { _mutex.Release(); } } } 
+1
source share


It depends on what kind of fantasy you want to get. One simple way is to save the task and link subsequent tasks (with little synchronization):

 public class AsynchronousWrapper { private Task previousTask = Task.CompletedTask; private SynchronousProcessor proc = new SynchronousProcessor(); public Task<string> ProcessAsync(string arg) { lock (proc) { var task = previousTask.ContinueWith(_ => proc.Process(arg)); previousTask = task; return task; } } } 
0
source share

Source: https://habr.com/ru/post/1015671/


All Articles