0
    For Each account In _accounts11
        Dim newtask = account.readbalancesAsync()
        newtask = newtask.ContinueWith(Sub() account.LogFinishTask("Getting Balances", starttime))
        newtask = newtask.ContinueWith(Async Function() account.getOrdersAsync())
        newtask = newtask.ContinueWith(Sub() account.LogFinishTask("Getting Orders", starttime))

        tasklist.Add(newtask)
    Next


    Await Task.WhenAll(tasklist.ToArray)
    Dim b = 1

Basically, for each account, I want to do account.readbalancesAsync and after that, I want to do account.getOrdersAsync()

I left code newtask.ContinueWith(Sub() account.LogFinishTask("Getting Balances", starttime)) to show I know how ContinueWith works. However, after that, I need to continue with another task.

How do I do so?

What I am trying to do is something like this

    For Each account In _accounts11
        await account.readbalancesAsync()
        account.LogFinishTask("Getting Balances", starttime)
        await account.getOrdersAsync())
        account.LogFinishTask("Getting Orders", starttime)

        tasklist.Add(newtask)
    Next

Obviously, if I do it like this, then one account have to wait for another account to finish. I want all accounts to run parallelly.

Or let's take a look at this code

dim response1 = await client.GetAsync("http://example.com/");
dim response2 = await client.GetAsync("http://stackoverflow.com/");

Say I do it like this

dim newtask = client.GetAsync("http://example.com/").continueWith(....)
await newtask

What should I put in ....

user4951
  • 32,206
  • 53
  • 172
  • 282
  • You're already starting continuation tasks several times. What's stopping you from continuing calling `ContinueWith`? Though I'd really like to know why you are using all these continuation tasks in the first place, instead of just wrapping everything in a `Sub()`/`End Sub` lambda block? Since each task is already run in the background there's no point in creating more of them than what's necessary. – Visual Vincent Apr 29 '19 at 20:15
  • Basically, for each account, I want to do await account.readbalancesasync, followed by await account.getordersasync. However, I want them to run parallelly rather than serially. – user4951 Apr 29 '19 at 20:20
  • I know how to use continueWith with non blocking normal function. Now I want to use continueWith with an async function. – user4951 Apr 29 '19 at 20:28

2 Answers2

3

I think you've mistakenly taken a wrong turn somewhere. If you need to run those four statements after each other, but without interfering with the loop, all you need to do is to create one task executing a multiline/block lambda expression.

For instance:

For Each account In _accounts11
    Dim newtask = Task.Run( 'Start a new task.
        Async Function() 'Multiline lambda expression.
            Await account.readbalancesAsync()
            account.LogFinishTask("Getting Balances", starttime)
            Await account.getOrdersAsync()
            account.LogFinishTask("Getting Orders", starttime)
        End Function
    ) 'End of Task.Run()

    tasklist.Add(newtask)
Next
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • That's a way. Can you do it with continueWith instead? – user4951 Apr 29 '19 at 20:33
  • 1
    @user4951 : May I ask why? `ContinueWith` isn't meant to be used this way, and starting several new tasks like that will impact performance (even if it might not be noticable, it is highly unnecessary). – Visual Vincent Apr 29 '19 at 20:33
  • I think it's kind of strange. I can use continueWith with normal function but I can't use it with awaitable function. Also is task.run run on the same thread? – user4951 Apr 29 '19 at 20:34
  • @user4951 : Please describe _why_ you can't use `ContinueWith` with an awaitable function. What do you expect to happen, and what happens instead? -- Also, no: `Task.Run()` starts a completely new task, running in whatever thread(s) the OS decides it to run it (remember: tasks might switch between different threads, but you can be sure that it will always be run in the background - so _not_ in the same thread). – Visual Vincent Apr 29 '19 at 20:36
  • regular async await thingy is run on the foreground. You said I can use ContinueWith with an awaitable function. How exactly I should do that? Thanks. – user4951 Apr 29 '19 at 20:42
  • @user4951 : No it is not. `Async/Await` roughly works by giving control back to the calling thread the second you hit `Await`. This allows the calling thread to continue doing work until a "response" has been received and the `Await` is finished. The calling thread is then instructed to continue executing the code located after the `Await`. If you use `ContinueWith` or a separate task it will run in one or more secondary threads. -- Edited the answer to include a `ContinueWith` example. – Visual Vincent Apr 29 '19 at 20:47
  • Oh great. Thanks. So we use async sub, not async function? you sure? – user4951 Apr 29 '19 at 20:49
  • @user4951 : As I said I have not tested the code, but both `Function`s and `Sub`s should work fine. Though I guess technically it should be a `Function` since `Await` usually returns a `Task`. – Visual Vincent Apr 29 '19 at 20:55
  • @user4951 : For the record if you're looking to run your code in parallel while still being able to _directly_ access the user interface (main thread) then that's not possible. The very definition of doing something in parallel is to divide work between separate entities. For a computer, this is threads. You cannot run two processes simultaneously in the same thread. If you need to run something in parallel you need multithreading, and if you need to access the UI from a background thread, you need to [invoke](https://stackoverflow.com/a/45571728). – Visual Vincent Apr 29 '19 at 20:55
  • I understand. I think my code will all run in the main thread. Something else may run on another thread. That's how async await differ from other multi threading approach. – user4951 Apr 29 '19 at 20:58
  • There's definitely some compiler warning saying, Why don't you try that yourself before I select your answer. I upvoted yours already – user4951 Apr 29 '19 at 20:59
  • @user4951 : `Async/Await` is not about multithreading. It is literally just about doing something else while you wait for your first process to be finished. Whether that'd be fetching something from a database/server or performing calculations, it doesn't matter. It has little or nothing to do with multithreading. _Tasks_ on the other hand do, in a way. – Visual Vincent Apr 29 '19 at 21:01
  • @user4951 : Unfortunately I'm on my phone so I cannot test it (switch to `Function`s if it's that it's complaining about), and I'm also not entirely sure what your goal is with this and how you want it to work. There doesn't however seem to be a good reason for you to be using `ContinueWith`. It just seems wasteful. – Visual Vincent Apr 29 '19 at 21:09
  • @user4951 : I tried the `ContinueWith` example at dotnetfiddle and decided to scrap it, because I couldn't get it to work. This simply isn't how `Async` methods are supposed to be used, and there's no good reason for you to be doing it this way. Like I said, it's impossible to run work in parallel in only one thread. Even `ContinueWith` creates a new task running in (a) separate thread(s). If you need to access controls or components in your user interface (the main thread) from these parallel processes, your only option is to do so by [invoking](https://stackoverflow.com/a/45571728). – Visual Vincent Apr 29 '19 at 21:31
1

I just want to add something to VisualVincent's answer. I still prefer to do this with continueWith

Private Async Function readBalancesAndOrderForEachAccount(starttime As Long) As Task
    Await readbalancesAsync()
    LogFinishTask("Getting Balances", starttime)
    Await getOrdersAsync()
    LogFinishTask("Getting Orders", starttime)
End Function

Public Shared Async Function getMarketDetailFromAllExchangesAsync2() As Task
    Dim CurrentMethod = MethodBase.GetCurrentMethod().Name
    Dim tasklist = New List(Of Task)
    Dim starttime = jsonHelper.currentTimeStamp

...

        For Each account In _accounts11
            Dim newtask = account.readBalancesAndOrderForEachAccount(starttime)
            tasklist.Add(newtask)
        Next
        Await Task.WhenAll(tasklist.ToArray)
        Dim b = 1
   ...
    End Function

That seems work. However, I want to understand how to do this using continueWith because I am very curious.

user4951
  • 32,206
  • 53
  • 172
  • 282
  • If you move the `LogFinishTask` call inside the respective methods, you can simply have: `Dim newtask As Task = readbalancesAsync().ContinueWith(Async Function(t) Return Await getOrdersAsync() End Function)`. `getOrdersAsync()` can return a `Task(Of Boolean)`: you just return `True` or `False`. Note that, in VB.Net, the `Function(t)` lambda must be written in 3 lines. – Jimi Apr 29 '19 at 20:51
  • 1
    You shouldn't use `ContinueWith`; it's a [low-level, dangerous method](https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html). `Await` is much better in every way. – Stephen Cleary Apr 29 '19 at 23:11
  • ContinueWith works perfectly for me. I just can't do it with awaitable functions – user4951 Apr 30 '19 at 17:18