By default, tasks has no affinity in TBB. After the task is allocated, and before it is spawned, one can use set_affinity() to specify desired affinity_id. Each worker thread has some affinity_id associated with it. When the task with non-zero affinity_id is spawned, a notification is "mailed" to the thread with the same id, so that it could take this task when its own pool is empty. The affinity is "soft" however, which means that the task is not guaranteed to execute on the thread with givenaffinity_id; it also can be taken for execution by the thread that created it, or it can be stolen by a third thread, whatever happens first. There are some additional heuristics used in the scheduler to increase chances for affinitized execution, e.g. affinitized tasks could be omitted during stealing.
get_task() does not take affinity_id into account, it just takes a task from the pool of the current thread. So locally spawned tasks take priority over affinitized tasks, which take priority over stealing a task from another thread.
I have an additional question here. I have read the spawn() in the task.cpp. In this function, it use the affinity_id to identify the corresponding mailbox. Does this means that the affinity_id is something like the thread id? However, I ran the seismic example provided in TBB package and find out that the affinity_id is ranged from 0 to 4 on my dual core laptop. How does the affinity_id maps to the exact thread context?
While currently there is 1-1 mapping between affinity_id and mailboxes, in future it might change. E.g. we may adjust affinity rules for multicore processors with shared L2 cache.
Think of affinity_id as the hint you provide to the scheduler that some task shares data with an earlier executed task. You get the affinity_id via note_affinity, record it, and provide as the hint later. E.g. affinity_partitioner for parallel loops in TBB basically works this way.