locked
why are returned Task.IDs not unique

    Question

  • Can someone please explain the logic by which Task.ID is populated by the framework? I was surprised to find out that these are not unique among different tasks thus they can't be used for identifying anything. Here are my observations:

    When submitting multiple tasks, and the tasks are not finished by the time they are awaited then they do get incremental unique IDs. Given the same case but if at least one task is complete before it is awaited, the ID of that tasks get reused for another task. In my case I have 4 tasks sent simultaneously being awaited with Task.WhenAny(). Since two of the four completed before being awaited they are sharing the ID of the two outstanding. Why is this? And what mechanism can be used to truly identify completed tasks? 

    Sunday, November 25, 2012 5:52 PM

Answers

  • I see (so, just to be clear for other readers, this has nothing to do with IDs or reuse of IDs.). Yes, it's perfectly valid for a method to return the same Task object multiple times; there's no requirement that a method must return a new Task object on each invocation.  This is true regardless of whether the Task is completed or not.  For example, I could implement a method like:

    public Task<string> DownloadAsync(string url)
    {
        Task<string> result;
        if (!m_cache.TryGetValue(url, out result))
        {
            result = new HttpClient().GetStringAsync(url);
            m_cache.Add(url, result);
        }
        return result;
    }
    private Dictionary<string,Task<string>> m_cache;

    Here my method is able to have just one Task<string> per URL, such that it can hand out the same reference to the downloaded or downloading content, even if it's not completed yet.

    In short, you can't rely on the ID of Tasks handed back from a method being different, because they may actually be the exact same object. As such, whatever you were trying to do with the ID in this particular case, you'll need to come up with another solution. If you're having trouble coming up with an approach, feel free to start another forum thread and I or others can try to help with a solution.

    Wednesday, November 28, 2012 4:52 PM
    Moderator

All replies

  • Hi Perry-

    Tasks do get unique IDs, at least within the range of Int32, meaning that IDs could start to be reused if you have more than ~4 billion tasks.  IDs are not reused in the manner that you say, e.g. IDs aren't recycled just because the task completes. Can you share a repro of where you're seeing duplicate IDs?

    (Keep in mind that an API could cache a Task and return that same Task object to multiple callers if doing so makes sense, e.g. a ReadAsync API might return a cached Task<int> when the result is synchronously known to be 0, in which case every caller will see the same ID for that Task because it's the same Task object.)

    Sunday, November 25, 2012 6:45 PM
    Moderator
  • Yea, I think that is exactly what's happening. If a tasks completes before the await gets encountered it seems to be placed back in the reusable task cache. So the next async method I call, even if it is immediately after, gets a cached task with the same ID.

    Dim objTasks As New List(Of Task)
    
    For Each objRoute As Route In Me.Routes 'this iterates only 3 times
        Dim objTask As Task = DoWork()
        
        'on the second iteration I get the same Tasks object
        'with the same ID if DoWork finishes to fast	
        objTasks.Add(objTask)
    Next
    
    While objTasks.Count > 0
        Dim objTask As Task = Await Task.WhenAny(objTasks)
        RouteComplete(objTask)
        objTasks.Remove(objTask)
    End While




    Monday, November 26, 2012 4:24 AM
  • Can you supply a standalone repro?  As I mentioned, there is no reuse of task IDs in the manner you suggest; unique Task objects will not reuse Int32 IDs until the full Int32 range has been exhausted.
    Monday, November 26, 2012 8:27 AM
    Moderator
  • I didn't say unique task objects are reusing the ID. I said I am not getting unique task objects returned from my multiple calls in the loop to the same awaitable method. I was expecting three calls to the method each returning a unique task object, hence each with a unique ID. But instead, as your previous answer stated, I am getting cached tasks back. Since they are cached I am getting the same task object and task ID back from two different awaitable method calls returning the same boolean result. So I can't use the ID from the completed Task to determine which of my calls has been completed because the Task/ID combination is not unique among calls. That's the problem.
    Tuesday, November 27, 2012 8:35 AM
  • I see (so, just to be clear for other readers, this has nothing to do with IDs or reuse of IDs.). Yes, it's perfectly valid for a method to return the same Task object multiple times; there's no requirement that a method must return a new Task object on each invocation.  This is true regardless of whether the Task is completed or not.  For example, I could implement a method like:

    public Task<string> DownloadAsync(string url)
    {
        Task<string> result;
        if (!m_cache.TryGetValue(url, out result))
        {
            result = new HttpClient().GetStringAsync(url);
            m_cache.Add(url, result);
        }
        return result;
    }
    private Dictionary<string,Task<string>> m_cache;

    Here my method is able to have just one Task<string> per URL, such that it can hand out the same reference to the downloaded or downloading content, even if it's not completed yet.

    In short, you can't rely on the ID of Tasks handed back from a method being different, because they may actually be the exact same object. As such, whatever you were trying to do with the ID in this particular case, you'll need to come up with another solution. If you're having trouble coming up with an approach, feel free to start another forum thread and I or others can try to help with a solution.

    Wednesday, November 28, 2012 4:52 PM
    Moderator