Fully thread-safe shared_ptr implementation

后端 未结 9 1448
面向向阳花
面向向阳花 2021-02-02 17:02

Does anybody know of a fully thread-safe shared_ptr implementation? E.g. boost implementation of shared_ptr is thread-safe for the targets (refcounting

9条回答
  •  伪装坚强ぢ
    2021-02-02 17:47

    Adding the necessary barriers for such a fully thread-safe shared_ptr implementation would likely impact performance. Consider the following race (note: pseudocode abounds):

    Thread 1: global_ptr = A;

    Thread 2: global_ptr = B;

    Thread 3: local_ptr = global_ptr;

    If we break this down into its constituent operations:

    Thread 1:

    A.refcnt++;
    tmp_ptr = exchange(global_ptr, A);
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 2:

    B.refcnt++;
    tmp_ptr = exchange(global_ptr, B);
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 3:

    local_ptr = global_ptr;
    local_ptr.refcnt++;
    

    Clearly, if thread 3 reads the pointer after A's swap, then B goes and deletes it before the reference count can be incremented, bad things will happen.

    To handle this, we need a dummy value to be used while thread 3 is doing the refcnt update: (note: compare_exchange(variable, expected, new) atomically replaces the value in variable with new if it's currently equal to new, then returns true if it did so successfully)

    Thread 1:

    A.refcnt++;
    tmp_ptr = global_ptr;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
        tmp_ptr = global_ptr;
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 2:

    B.refcnt++;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, A))
        tmp_ptr = global_ptr;
    if (!--tmp_ptr.refcnt) delete tmp_ptr;
    

    Thread 3:

    tmp_ptr = global_ptr;
    while (tmp_ptr == BAD_PTR || !compare_exchange(global_ptr, tmp_ptr, BAD_PTR))
        tmp_ptr = global_ptr;
    local_ptr = tmp_ptr;
    local_ptr.refcnt++;
    global_ptr = tmp_ptr;
    

    You've now had to add a loop, with atomics in it in the middle of your /read/ operation. This is not a good thing - it can be extremely expensive on some CPUs. What's more, you're busy-waiting as well. You can start to get clever with futexes and whatnot - but by that point you've reinvented the lock.

    This cost, which has to be borne by every operation, and is very similar in nature to what a lock would give you anyway, is why you generally don't see such thread-safe shared_ptr implementations. If you need such a thing, I would recommend wrapping a mutex and shared_ptr into a convenience class to automate locking.

提交回复
热议问题