2

I'm trying to do some asynchronous I/O work detached from UI thread. Somewhere I read:

  • 1) For CPU-bound code, you await an operation which is started on a background thread with the Task.Run method. Such as calculating prime numbers
  • 2) For I/O-bound code, you await an operation which returns a Task or Task inside of an async method. Such as waiting for network or database

So I did this:

// in windows form UI
private async void btnImport_Click(object sender, EventArgs e) {
    // [...]
    List<DataRow> rows = await importer.ImportDataAsync(123, 456);
    // [...]
}

// in Importer.ImportDataAsync:
public async Task<List<DataRow>> ImportDataAsync(int parent, int child, CancellationToken token = default(CancellationToken)) {

    // [...]
    List<DataRow> list = await RealImportFromDB(parent, child);
    return list;
    // [...]
}


public List<DataRow> RealImportFromDB(int p, int c) {

    List<DataRow> rowList;
    // here fetch the rows from DB over slow network
    // and return the row list
    return rowList;
}

With this approach the UI is blocked. If I call RealImportFromDB(...) like this

List<DataRow> l = await Task.Run(() => RealImportFromDB(parent, child));

the UI is not blocked but that conflicts with point 2) from above IMHO.

Where do I do things wrong?

Best regards, alex

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
alex999
  • 105
  • 1
  • 8
  • 2
    You haven’t shown how the data is read from the database. If it’s synchronous methods then it will naturally block. – Sami Kuhmonen Sep 15 '18 at 10:29
  • Possible duplicate of [Calling async method on button click](https://stackoverflow.com/questions/28601678/calling-async-method-on-button-click) – Dmitry Pavlov Sep 15 '18 at 10:34
  • 1
    How are you awaiting `RealImportFromDB` when it does not return a `Task`? It should also be `async` and should be awaiting calls to async methods against the DB. When using async/await it should go all the way from the top (in your case the button click handler) to the bottom (calls out to the DB). – juharr Sep 15 '18 at 10:35
  • Not sure how you are even getting a run time error because that won't even compile. You can't await a method that does not return a `Task` or `Task` (await RealImportFromDB). – Igor Sep 15 '18 at 10:40

2 Answers2

1

public List<DataRow> RealImportFromDB(int p, int c) is a blocking call to the database, so to execute it Asynchronously, you have used the #1, where you have wrapped the call inside the Task.Run, which will free up Ui thread as expected

With this approach the UI is blocked. If I call RealImportFromDB(...)

It is since the method is not meant for the Asynchronous calling, it doesn't return a Task or Task<T>, which is the common requirement to make the Async call

Your code, await RealImportFromDB(parent, child) is not correct that's a compilation error, since you can only await the calls, which implements the GetAwaiter()internally check (this and this) and most common scenario is to return Task or Task<T>, there are other types too

Let's try to understand your two statements:

1) For CPU-bound code, you await an operation which is started on a background thread with the Task.Run method. Such as calculating prime numbers

This is what you are currently doing and is often done in clients to free up Ui thread, while processing takes place in background, but this would still use a Thread pool thread to do the execution, which is not as important as Ui thread, but still system resource

2) For I/O-bound code, you await an operation which returns a Task or Task inside of an async method. Such as waiting for network or database

To implement this you need a method, which is Async by default and returning a Task or Task<T>, such methods are part of all data frameworks, for every sync method nowadays there's a corresponding async method to initiate a asynchronous execution and they are the true IO calls, where they don't use a thread, since the processing is not in the same process, its across network / process boundary, so calling thread needn't wait, it just needs to come back and pick the result, when it arrives (any thread pool thread, not necessary the dispatching thread). Internally such methods use TaskCompletionSource<T> (When to use TaskCompletionSource), which has mechanism to notify the caller when the network call has accomplished

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
0

To implement this you need a method, which is Async by default and returning a Task or Task

Thanks a lot, this was my trouble. My RealImportFromDB(...) method is not an async method since it deals with an older, proprietary library that seems not ready for async calls.

Those were my thougths: with awaiting the result from ImportDataAsync(...) everything that is called within (e.g. RealImportFromDB(...)) is dispatched from the UI thread also. So to say: everything within ImportDataAsync(...) is encapsulated / runs on in the second, non-blocking thread.

@others: yes you are right, the sample from my code won't even compile. Fiddled around a lot, so the code sample does not show everything what was changed, sorry for that :-}

alex999
  • 105
  • 1
  • 8