pthread_create 在构造函数中出现段错误

pthread_create in constructor segfault

我发现,在下面的这个小例子中,如果我在我的结构的构造函数中调用 pthread_create,我会在调用 pthread_mutex_lock() 时随机出现段错误。

有时第一位哲学家的名字字段是空的。

如果我将 pthread_create 移动到构造函数之后的 运行() 函数,则不会出现段错误。

似乎对 pthread_create 的调用发生在所有成员初始化之前。 class 的成员初始化列表不应该在调用构造函数体之前完成吗?

感谢任何提示!

clang 版本 9.0.0 (tags/RELEASE_900/final) 目标:x86_64-apple-darwin17.7.0

此致,乔治

#include <array>
#include <iostream>
#include <pthread.h>
#include <string>
#include <vector>

using namespace std;


struct chopstick
{
        pthread_mutex_t mutex;
        chopstick()
        {
                pthread_mutex_init(&mutex,nullptr);
        }
        ~chopstick()
        {
                pthread_mutex_destroy(&mutex);
        }

};

void* feed(void* data);
struct philosopher
{
        pthread_t thread;
        string name;
        unsigned mouthfuls;
        chrono::seconds sec;
        chopstick &left, &right;

        pthread_t& get_thread() { return thread; }

        philosopher(const string &s, chopstick &l, chopstick &r): name(move(s)), left(l), right(r), mouthfuls(0)
        /*
                enable below to avoid segfault
        {}
        void run()  
        */
        {
                pthread_create(&thread, nullptr, feed, this);
        };

};

void* feed(void* data)
{
        philosopher & a = *static_cast<philosopher*>(data);
        while (a.mouthfuls < 20)
        {
                pthread_mutex_lock(&a.left.mutex);
                pthread_mutex_lock(&a.right.mutex);
                cout << "Apostle " << a.name << " thread id " << pthread_self() 
                        << " acquired a chopstick at count: " << a.mouthfuls << endl;
                ++a.mouthfuls;
                pthread_mutex_unlock(&a.right.mutex);
                pthread_mutex_unlock(&a.left.mutex);
        }
        return nullptr;
}

int main (int argc, char const * argv[])
{
        array<string, 12> names {"John", "Thaddeus", "Simon Peter", "James", "Judhas", "Bartholomew", "Matthew", "Philip", "Simon Zealot", "Thomas", "Andrew", "James the Lesser" };
        array<chopstick,names.size()> sticks;
        vector<philosopher> philosophers;

        for (int i=0; i+1<names.size(); ++i)
                philosophers.emplace_back( names[i],sticks[i],sticks[i+1] );
        philosophers.emplace_back(names[names.size()-1], sticks[0],sticks[names.size()-1]);
        //for (philosopher&  a: philosophers) a.run();  //<-- enable to avoid segfault
        for (philosopher&  a: philosophers) pthread_join(a.get_thread(), nullptr);

        return 0;

}

std::vector 会在代码执行 philosophers.emplace_back() 时调整大小,这会移动内存中的元素,因此它们之前的地址变得无效,并且 feed() 函数最终会使用它们访问对象旧的无效地址。

解决方法是使 philosopher class 不可复制且不可移动,然后使用 std::list<philosopher>std::forward_list<philosopher> 代替 std::vector<philosopher>. std::liststd::forward_list 不会移动内存中的元素,因此能够存储不可复制和不可移动的对象。

您可能还喜欢使用 std::thread 代替 pthread_t,并使用 std::mutex 代替 pthread_mutex_tstd classes 是 non-copyable/movable 这将防止您在编译时出现此错误。此外,代码不会检查 pthread 函数的 return 值是否有错误,而 std::threadstd::mutex 会为您检查。