2

I have an abstract class whose method is define as like this

public abstract class FooBase : IDisposable { protected abstract bool GetData(params object[] getDataParams); } 

I am using this abstract class like this and trying to run the GetData method asynchronously

public class SomeClass : FooBase { protected override bool GetData(params object[] getDataParams) { var task = Task.Factory.StartNew(() => { using (var adapter = new DataAdapter()) { // Take your time and fetch the data } }); task.Wait(); return true; } } 

What I am trying to find out is: Am I doing it right or wrong, or if there is any other better way to achieve the same goal.

Update

If i change my method like this would it be called asynchronously or not

public class SomeClass : FooBase { protected override bool GetData(params object[] getDataParams) { var task = Task.Factory.StartNew(async () => { using (var adapter = new DataAdapter()) { // Take your time and fetch the data } }); task.Wait(); return true; } } 

Appreciate your comments on this.

6
  • 3
    Your usage isn't asynchronous - you're starting the task in a background thread but then the main thread is just sitting and waiting for it. Return the task directly or await the task or similar. The method in your abstract class is synchronous by definition so unless you can change that you're out of luck. Commented Jan 18, 2017 at 9:54
  • see TaskCompletionSource Commented Jan 18, 2017 at 10:00
  • 2
    Please post your actual data access code, not the wrappers. ADO.NET has asynchronous methods like ExecuteReaderAsync and ExecuteNonQueryAsync. ORMs like EF and microORMs like Dapper also provide asynchronous methods. Most likely your actual data access code can be modified to run asynchronously. Commented Jan 18, 2017 at 10:08
  • 1
    Re: your update - your main thread is still blocking on this method and it always well be until your method signature changes. Can you explain what you're trying to achieve here? Are you just trying to avoid blocking a UI thread or are you actually expecting OS-level asynchrony? Commented Jan 18, 2017 at 10:57
  • 1
    Ok, well as Richard's answer points out you can't achieve OS-level asynchrony by wrapping methods - that won't change what's happening under the hood. You need to use an asynchronous API for that. Regarding not blocking the UI thread, in order to achieve that you need to change the method signature for GetData so it, too, returns a task (either by returning the wrapped task directly or using async/await) - you'll need to do this all the way up to your UI otherwise the UI thread will still be blocked somewhere down the line. Commented Jan 18, 2017 at 11:14

1 Answer 1

3

There is no way to run a synchronous method other than synchronously.

You can wrap the result in something that looks like it was run asynchronously (eg. Task.FromResult), or run in another thread.1 But the synchronous method will still block the thread it is running on.

(The reverse, blocking on an asynchronous operation, is trivial. This is why you need to have the underlying operations asynchronous because you can build both synchronous and asynchronous methods on top.)

Update (for update in question):

That additional code – specifically the task.Wait() statement – will cause the caller's thread to block while waiting for the task to complete. That task will run on another thread causing that thread to block. Ie. you are causing two threads (caller, and a thread pool thread) to block. If the underlying method was called directly only the caller's thread would be blocked.

You have two approaches:

  1. Best: use ADO.NET's asynchronous operations. (This means not using DataTables/DataAdaptor but IMHO that's a good move anyway: all they do is move operations that should be done on the database into the client.)

  2. Offload to another thread, but return a Task<TResult> to the caller, only marking the Task as complete then the underlying operation is complete. Something like:

    protected override Task<bool> GetData(params object[] getDataParams) { var tcs = new TaskCompletionSource<bool>(); Task.Factory.StartNew(async () => { using (var adapter = new DataAdapter()) { // Take your time and fetch the data tcs.SetResult(the-result); } }); return tcs.Task; } 

    Note here the return from GetData is a Task<bool>: the caller needs to either wait or do something else concurrently and then get the result of the data operation. If the caller just waits then you have two blocked threads. (C# 5's await is different: the caller also becomes asynchronous.)


1 For the avoidance of doubt: this can be made to look like a normal, Task<T> returning, asynchronous method (eg. making use of TaskCompletionSource<T> off loading the blocking from the current thread to some thread in the thread pool). But it is still a blocking operation: just blocking a different thread.

Sign up to request clarification or add additional context in comments.

6 Comments

I think the terminology gets a little hazy here - I'd say that a Task wrapping a background thread can be considered asynchronous (at least from the perspective of the consumer).
@AntP not quite - wrapping a blocking call with a Task is what's called fake asynchrony. Asynchrony (not waiting for completion) isn't the same as concurrency (executing many things in parallel). Task.Run will start a concurrent task that runs a synchronous job. A BeginExecute or equivalent async method though, pass the request to the asynchronous driver level and don't consume threads at all. This can be a huge benefit for web applications or high-throughput servers
@PanagiotisKanavos I understand that but it's a matter of semantics that matters little to a consumer - and particularly given that typical naming convention for methods returning tasks dictates that they be appended with Async, not Concurrent, whether the task is asynchronous at the lowest level doesn't matter a great deal in this context.
@AntP it's not semantics at all. It's a fundamental difference that does matter to consumers in a very basic way. It means that Task.Run(()=> cmd.ExecuteReader()) is fundamentally different from await cmd.ExecuteReaderAsync(). In the end, it means that the same server can handle a lot (multiples, not percentages) more requests if you properly use asynchronous programming. It also results in cleaner and safer code.
@PanagiotisKanavos Again - I understand all of this already. What I am getting at is that "asynchrony" is widely (and generally unambiguously) used with respect to a specific context (i.e. if it doesn't block the context, it is asynchronous with respect to that context - however leaky a concept that is). I'm not sure that the distinction between that and "true" OS-level asynchrony is relevant to this question (although rereading it it might well be a distinction that the OP is unaware of).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.