locked
determining which async task item finished RRS feed

  • Question

  • I am starting several tasks at once each coinciding with a objRoute object. As each task finishes I need to know which objRoute triggered each task so I can run some finalization code on the objRoute with RouteComplete(). As a solution for this, I added a Dictionary that keeps track of the task to route relationships, keyed on the task instance, so I can look up the route as each task completes. To my surprise the dictionary errors out stating that duplicate keys can not be inserted. The error occurs because the objTask instances are not unique as they are returned by DoWork(). What other technique can I use to figure out what objRoute each completed task was generated by? Is there a best practice pattern I am missing?

    Dim objTasks As New List(Of Task)
    Dim objRouteLookup As New Dictionary(Of Task, Route)
    
    For Each objRoute As Route In Me.Routes
        Dim objTask As Task = DoWork()
        objTasks.Add(objTask)
        objRouteLookup.Add(objTask, objRoute)
    Next
    
    While objTasks.Count > 0
        Dim objTask As Task = Await Task.WhenAny(objTasks)
        RouteComplete(objRouteLookup(objTask))
        objTasks.Remove(objTask)
    End While



    Sunday, November 25, 2012 3:20 AM

Answers

  • I don't see any reason why you would need a dictionary for that. You have plenty options to do something when a Task finishes without that.

    One option would be to use ContinueWith() and use a lambda as its parameter. This way, you reference the correct route from inside the continuation.

    I think a better option is to create another Async function, that will call RouteComplete() after DoWork() finishes:

    Private Async Function DoWorkAndComplete(ByVal route As Route) As Task
        Await DoWork(route)
        RouteComplete(route)
    End Function

    When you do this, you can then remove simplify your code:

    Dim tasks As New List(Of Task)
    
    For Each route As Route In Me.Routes
        Dim task As Task = DoWorkAndComplete(route)
        tasks.Add(task)
    Next
    
    Await Task.WhenAll(tasks)

    (I also removed all those obj- prefixes from your variable names, I don't see any use for them.)

    My code assumes that RouteComplete() is thread-safe and can be called from multiple threads at the same time, if that's not true, you might need some other solution.

    • Marked as answer by Perry Manole Sunday, November 25, 2012 6:07 PM
    Sunday, November 25, 2012 11:59 AM

All replies

  • Why don't you hold the Id of the Task in your dictionary instead of the complete Task as key ?, Similar like this :

    objRouteLookup.Add(objTask.Id, objRoute)


    - Ram


    Sunday, November 25, 2012 8:35 AM
  • Ram, if the Tasks themselves are not unique, their ids are also not going to be unique, so your solution won't work.
    Sunday, November 25, 2012 11:40 AM
  • I don't see any reason why you would need a dictionary for that. You have plenty options to do something when a Task finishes without that.

    One option would be to use ContinueWith() and use a lambda as its parameter. This way, you reference the correct route from inside the continuation.

    I think a better option is to create another Async function, that will call RouteComplete() after DoWork() finishes:

    Private Async Function DoWorkAndComplete(ByVal route As Route) As Task
        Await DoWork(route)
        RouteComplete(route)
    End Function

    When you do this, you can then remove simplify your code:

    Dim tasks As New List(Of Task)
    
    For Each route As Route In Me.Routes
        Dim task As Task = DoWorkAndComplete(route)
        tasks.Add(task)
    Next
    
    Await Task.WhenAll(tasks)

    (I also removed all those obj- prefixes from your variable names, I don't see any use for them.)

    My code assumes that RouteComplete() is thread-safe and can be called from multiple threads at the same time, if that's not true, you might need some other solution.

    • Marked as answer by Perry Manole Sunday, November 25, 2012 6:07 PM
    Sunday, November 25, 2012 11:59 AM
  • Thanks for this. I am still trying to get my head around this new async/await idea.

    What is concerning is that Task.ID is not unique. But maybe that's a question for another thread.

    Regarding comment above: RouteComplete() is not thread safe, because it MUST run on the thread that iterates the routes due to reasons imposed by the design. But I think your solution still runs RouteComplete() on the desired thread. 


    Sunday, November 25, 2012 5:40 PM
  • What thread RouteComplete() will run on depends on what current the SynchronizationContext is, if any. If it's something like the WPF or Winforms context, then yeah, RouteComplete() will run on the UI thread.
    Sunday, November 25, 2012 7:06 PM
  • No, the entire code is running as a dispatched lambda on a static background thread managed by a Dispacher.Run() message pump. I think that's OK since there is only one thread involved.
    Monday, November 26, 2012 4:37 AM