【问题标题】:Unexpected number output意外数字输出
【发布时间】:2021-09-12 07:25:41
【问题描述】:

我正在尝试创建一个程序来解决使用 posix 线程就餐哲学家的问题。但是,我一开始就卡住了,因为 std :: cout

pthread_mutex_t mutexSpoon[PHILOSOPHERS];

pthread_t createThread(int number){
    pthread_t id;
    int rc = pthread_create(&id, NULL, philFunc, &number);
    if(rc){
        abort();
    }
    return id;
}

void *philFunc(void *arg){
    srand(time(0));
    int id = *(int*)arg;
    int leftSpoon = (id>0) ? id-1 : PHILOSOPHERS;
    int rightSpoon = id;
    int temp;
    int i = 0;
    while(i < 10){
        usleep((200 - 50) * ( (double)rand() / RAND_MAX ) + 50);
        std::cout << id+1 << " PHILOSOPHER: thinking" << std::endl; ++i;

    }
    return nullptr;
}

main.cpp

using namespace std;
extern pthread_mutex_t mutexSpoon[PHILOSOPHERS];

int main(){
    setlocale(LC_ALL, "rus");


    for(int i = 0; i < PHILOSOPHERS; ++i)
        pthread_mutex_init(&mutexSpoon[i], NULL);


    vector<pthread_t> vecID(PHILOSOPHERS);

    for(int i = 0; i < PHILOSOPHERS; ++i)
        vecID[i] = createThread(i);

    for(int i = 0; i < PHILOSOPHERS; ++i)
        pthread_join(vecID[i], NULL);


    for(int i = 0; i < PHILOSOPHERS; ++i)
        pthread_mutex_destroy(&mutexSpoon[i]);
    return 0;
}

【问题讨论】:

  • pthread_create(&amp;id, NULL, philFunc, &amp;number); 中,您传递的是局部变量的地址。很可能线程只有在createThread 返回后才读取变量,然后&amp;number 成为悬空指针。
  • 你很可能不想在你的线程函数中使用srand(time(0));。您通常应该在程序开始时在main 中调用一次。
  • 您可能希望C++ thread support library 比 p-threads 更便携。
  • 请注意rand 可能不是线程安全的:stackoverflow.com/questions/6161322/…
  • 您可能错过了使用mutexSpoon 的行,prehistoricpenguin 想指出这一点

标签: c++ multithreading pthreads


【解决方案1】:

线程函数使用地址作为参数,您将其作为地址传递给函数createThread - number 的局部变量。参数的生命周期不应短于线程,因此与互斥锁完全相同。使用您的 sn-ps 作为基础,我创建了一个解决该问题的示例:

#include <iostream>
#include <cstdlib>
#include <vector>
#include <pthread.h>
#include <unistd.h>

void *philFunc(void *arg);

#define PHILOSOPHERS 10

struct Philosopher {
    pthread_mutex_t mutexSpoon;
    pthread_t id;
    int no;
};

Philosopher philosophers[PHILOSOPHERS]  = {};

pthread_t createThread(int& number){
    pthread_t id;
    int rc = pthread_create(&id, NULL, philFunc, &number);
    if(rc){
        abort();
    }
    return id;
}

void *philFunc(void *arg){
    srand(time(0));
    int id = *(int*)arg;
    int leftSpoon = (id>0) ? id-1 : PHILOSOPHERS;
    int rightSpoon = id;
    int temp;
    int i = 0;
    while(i < 10){
        usleep((200 - 50) * ( (double)rand() / RAND_MAX ) + 50);
        std::cout << id+1 << " PHILOSOPHER: thinking" << std::endl; ++i;

    }
    return nullptr;
}

extern pthread_mutex_t mutexSpoon[PHILOSOPHERS];

int main(){
    setlocale(LC_ALL, "rus");

    for(int i = 0; i < PHILOSOPHERS; ++i) {
        pthread_mutex_init(&philosophers[i].mutexSpoon, NULL);
        philosophers[i].no = i;
        philosophers[i].id = createThread(philosophers[i].no);
    }
    
    for(int i = 0; i < PHILOSOPHERS; ++i)
        pthread_join(philosophers[i].id, NULL);


    for(int i = 0; i < PHILOSOPHERS; ++i)
        pthread_mutex_destroy(&philosophers[i].mutexSpoon);
    return 0;
}

如您所见,现在每个线程都有自己的结构Philosopher,按应有的方式存储其数据。虽然这里的 philosophers 是一个数组,但它可以是任何其他容器,只要它的元素存在足够长的时间并且不改变它们的地址(pthread 互斥体的某些实现的要求)。

请注意,createThread(int&amp; number) 现在通过引用获取其参数,因此表达式 &amp;number 将获取实际对象位置的地址,而不是局部变量的地址。

如果使用 C++ 线程支持和std::future,此代码会更简单。

【讨论】:

  • 感谢您的帮助!现在一切正常。
  • 小心。 srand 的放置至少会以两种不同的方式导致错误。
  • 这里mutexSpoon有什么用?好像没必要。
  • std::cout &lt;&lt; id+1 &lt;&lt; " PHILOSOPHER: thinking" &lt;&lt; std::endl; 这行不能保证这两部分会打印在一行中,我们可能会弄乱行。
  • @prehistoricpenguin 是的,它依赖于控制台输出的实现。如果将 cout 替换为任何其他流,它要么搞砸要么导致 UB(例如一些字符串流实现)。我只是没有更改与上述问题无关的代码中的任何其他内容。至于互斥锁,可能是 OP 没有向我们展示的东西,也许是输出同步。我把它作为与线程相关的数据保留了下来。代码不完整,一些变量根本没有使用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
相关资源
最近更新 更多