【问题标题】:C++11 shared_ptr and pthreadC++11 shared_ptr 和 pthread
【发布时间】:2018-11-20 20:54:53
【问题描述】:

我有一个使用 std::shared_ptr 作为参数的 API 库。

我想将这些 API 与 pthread 一起使用。

我正在做的是:从 shared_ptr 获取原始指针,以便将其传递给 pthread。 从原始的 shared_ptr 创建一个新的 shared_ptr 并从另一个线程调用我的 API。 但是,将原始指针转换回共享指针时出现double free or corruption 错误。

这是我的代码

#include <memory>
#include <iostream>
#include <thread>
#include <pthread.h>

void* print_task(void* ptr) 
{
    int* val_raw = static_cast<int*>(ptr);
    std::shared_ptr<int> val(val_raw);

    // CALL MY API WHICH TAKES A SHARED_PTR AS ARGUMENT
    std::cout<<"thread job done \n";
}

int main(int argc, char ** argv)
{

   pthread_t thread;

   std::shared_ptr<int> val = std::make_shared<int>(10);
   pthread_create(&thread, nullptr, &print_task, static_cast<void *>(val.get()));

   std::this_thread::sleep_for(std::chrono::seconds(5));

   return 0;
}

我想我在从共享指针到原始指针的所有转换中都做错了,因为使用 std::threads(我可以直接传递 shared_ptr)的相同代码可以工作。 但是我需要设置线程优先级,因此我尝试使用 pthreads 来做到这一点。

您知道如何更改我的代码以便能够传递共享指针并在 pthread 中使用它吗?

【问题讨论】:

  • 为什么不使用 C++ 线程?上次我查看时,他们公开了底层线程句柄,只是为了您需要做一些通用 API 不支持的特殊操作。
  • 为什么不直接传递一个指向 std::shared_ptr 的指针,然后在线程内复制构造另一个?
  • 问题可以简化为“如何通过 void 指针传递 shared_ptr?”线程是红鲱鱼,部分。
  • "将原始指针转换回共享指针时。" - 你不能那样做。你最终会得到两个单独的 shared_ptr——每个都有自己的引用计数,每个都为 0 并尝试删除对象。
  • 为什么要在main 中创建shared_ptr?您没有将shared_ptr 传递给线程,并且您不想在main 中管理对象的生命周期。

标签: c++ pthreads shared-ptr


【解决方案1】:

正如 cmets 中已经提到的,问题在于通过原始 void 指针传递共享指针,所以我现在将忽略线程部分:

// this is what we have and what we want to pass to the given function
shared_ptr<some_type> sptr;

// function to somehow pass the shared pointer to
void function(void* ptr);

// As always, when passing anything that doesn't fit into
// the raw pointer, we need to do dynamic allocation:
void* arg = new shared_ptr<some_type>(sptr);

// we can now pass this to the function as intended:
function(arg);

// Note that we give up ownership of the dynamically allocated
// shared pointer instance. Hence, the called function must
// release that object again (it takes ownership). The function
// therefore starts like this:
void function(void* ptr)
{
    // convert the typeless pointer to a typed pointer again
    shared_ptr<some_type>* psptr = static_cast<shared_ptr<some_type>*>(ptr);
    // move the content to a new, local instance
    shared_ptr<some_type> sptr = *psptr;
    // release the dynamically allocated shared pointer again
    delete psptr;
    /// ... code using sptr here ...
}

现在,虽然这可以保证有效,但在某些情况下,这可能不是最佳解决方案:

  • 首先,引用计数器的起伏不是免费的,特别是因为这是以线程安全的原子方式完成的。可以避免在函数内部复制共享指针然后删除复制的指针。只需创建为空实例并使用要复制的指针交换()它。假设 swap() 是专门的,这是一个安全的赌注,因为它是一个明显的优化,然后归结为交换两个原始指针。这种交换不必是线程安全的,因此速度更快。
  • 其次,动态分配代价高昂。您可以通过将原始对象的地址传递给函数来避免这种情况和手动释放,但是您必须保证在函数仍在执行时不会触及对象。尤其是线程,需要格外小心。

【讨论】:

    【解决方案2】:

    说实话,我认为这是一个棘手的小难题。您遇到的问题是,通过将原始指针传递给您的线程函数,您最终会得到两个 independant 共享指针管理同一个对象 而不是 两个 connected 共享指针共享所有权。

    因此两个共享指针试图删除它。

    有一个讨厌的生命周期问题如果你将一个指针传递给共享指针本身,因为你不能保证共享指针不会超出范围在新线程复制它之前。

    我通过传递从原始std::shared_ptr 创建的 动态分配std::shared_ptr 来解决这个问题,这保证了共享指针的控制块将一直存在,直到新线程尝试复制它。

    #include <memory>
    #include <iostream>
    #include <thread>
    #include <pthread.h>
    
    void* print_task(void* ptr)
    {
        // obtain the shared pointer from the dynamically created one guarantees
        // we will bee accessing a living control block preventing an
        // end-of-lifetime catastrophe
        std::shared_ptr<int> val = *static_cast<std::shared_ptr<int>*>(ptr);
    
        // DON'T FORGET TO DELETE THIS!!!
        // We had to allocate this dynamically to guarantee it lived until after
        // it was used.
        delete static_cast<std::shared_ptr<int>*>(ptr);
    
        // CALL MY API WHICH TAKES A SHARED_PTR AS ARGUMENT
        std::cout << "thread job done \n";
    
        return nullptr;
    }
    
    int main()
    {
        pthread_t thread;
    
        // create the shared resource. This MAY go out of scope
        // before the new thread copies it (however unlikely you
        // think that is).
        std::shared_ptr<int> val = std::make_shared<int>(10);
    
        // So instead of sending the shared pointer we create a NEW std::shared_ptr
        // which will keep the shared pointer's control block alive even if the original
        // shared pointer goes out of scope.
        pthread_create(&thread, nullptr, &print_task,
            static_cast<void*>(new std::shared_ptr<int>(val)));
    
        // Do other time consuming thread stuff here.
        std::this_thread::sleep_for(std::chrono::seconds(2));
    
    
        // Tidy up.
        void* ret = nullptr;
        pthread_join(thread, &ret);
    
        return 0;
    }
    

    当然,您现在在这里有一个 new/delete 对,但您仍然可以在线程之间传递共享所有权轮次。

    我想你甚至可以通过 temporary std::unique_ptr 采用传入的原始指针来摆脱 delete

    void* print_task(void* ptr)
    {
        // obtain the shared pointer from the dynamically created one guarantees
        // we will bee accessing a living control block preventing an
        // end-of-lifetime catastrophe
        std::shared_ptr<int> val = *std::unique_ptr<std::shared_ptr<int>>(static_cast<std::shared_ptr<int>*>(ptr));
    
        // CALL MY API WHICH TAKES A SHARED_PTR AS ARGUMENT
        std::cout << "thread job done \n";
    
        return nullptr;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-22
      • 2012-10-05
      • 2014-04-15
      • 1970-01-01
      • 2021-08-17
      • 2017-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多