【问题标题】:Odd behavior when creating many threads using a for loop使用 for 循环创建多个线程时的奇怪行为
【发布时间】:2016-10-29 00:57:17
【问题描述】:

我正在制作一个模拟一部电梯和 49 个人的多线程程序,每个人都有自己的线程和电梯。我正处于开始阶段,只是试图创建线程并验证它们是否正常工作。

我有一个名为person_threads 的pthread 数组,我试图在for 循环中对其进行初始化,在循环中我发送一条带有线程号的消息并在线程中打印出来。出于某种原因,我得到了奇怪的行为,几乎就像 for 循环在创建一些线程之前没有正确迭代(请参阅循环和输出)。每次运行时,这种行为都是随机的并且是多种多样的,我不确定我需要做什么才能正确创建线程。如果您对可能导致此问题的原因有任何见解,请提供帮助。

代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define MAX_PERSONS 49

void *person(void *myvar);
void *elevator(void *myvar);

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

    pthread_t elevator_thread;
    pthread_t person_threads[MAX_PERSONS]; //My array of threads

    char *elev_msg = "Elevator thread started";

    pthread_create(&elevator_thread, NULL, elevator, (void *) elev_msg);

    //Here is where I try to initialize messages and threads,
    //see Persons function and output    

    for (int i = 0; i < MAX_PERSONS; i++){
        char msg[50];
        snprintf(msg, sizeof msg, "Person thread %d started", i);
        pthread_create(&person_threads[i], NULL, person, (void *) msg);
    }

    printf("Main function after pthread_creates\n");

    pthread_join(elevator_thread, NULL);
    for (int i = 0; i < MAX_PERSONS; i++){
        pthread_join(person_threads[i], NULL);
    }

    return 0;
}

void *person(void *myvar){
    char *msg;
    msg = (char *) myvar;

    printf("%s\n", msg);

    return NULL;
}

void *elevator(void *myvar){
    char *msg;
    msg = (char *) myvar;

    printf("%s\n", msg);

    return NULL;
}

输出

brendan@brendan-Ubuntu-Desk:~/Documents/OS-Project2$ ./elevator 
Elevator thread started
Person thread 7 started
Person thread 7 started
Person thread 7 started
Person thread 7 started
Person thread 7 started
Person thread 7 started
Person thread 7 started
Person thread 8 started
Person thread 9 started
Person thread 10 started
Person thread 12 started
Person thread 12 started
Person thread 14 started
Person thread 15 started
Person thread 15 started
Person thread 16 started
Person thread 17 started
Person thread 18 started
Person thread 19 started
Person thread 20 started
Person thread 21 started
Person thread 22 started
Person thread 23 started
Person thread 24 started
Person thread 25 started
Person thread 26 started
Person thread 27 started
Person thread 28 started
Person thread 29 started
Person thread 30 started
Person thread 31 started
Person thread 32 started
Person thread 33 started
Person thread 34 started
Person thread 35 started
Person thread 36 started
Person thread 37 started
Person thread 38 started
Person thread 39 started
Person thread 40 started
Person thread 41 started
Person thread 42 started
Person thread 43 started
Person thread 44 started
Person thread 45 started
Person thread 46 started
Person thread 47 started
Person thread 48 started
Main function after pthread_creates
Person thread 48 started

请注意如何有多个 7、15、12 和 48。每次运行时,此行为都是随机的。我注意到它实际上总是创建 50 个线程,但我需要正确初始化数组。任何帮助将不胜感激。

【问题讨论】:

    标签: c linux multithreading pthreads


    【解决方案1】:

    原因如下:

    for (int i = 0; i < MAX_PERSONS; i++){
        char msg[50];
        snprintf(msg, sizeof msg, "Person thread %d started", i);
        pthread_create(&person_threads[i], NULL, person, (void *) msg);
    }
    

    msg参数只在主线程块范围内有效。您需要在堆上分配msg 缓冲区并在子线程中释放它。

    【讨论】:

      【解决方案2】:

      你的错误在这里:

        for (int i = 0; i < MAX_PERSONS; i++){
          char msg[50];
      

      由于您为消息缓冲区使用局部变量,因此在循环的每次迭代中,缓冲区都会在堆栈上创建然后销毁。然后在下一次迭代中,您可能会重用相同的内存并覆盖它。

      您需要使用malloc 为每个线程动态分配msg,以便它们都有自己的消息缓冲区,不会被共享和被覆盖。

      【讨论】:

      • @alk 我将保留您的编辑,但我故意将msg 声明加粗,当您将文本标记为代码时,您似乎无法强制使用粗体,这就是它不会缩进的原因。
      • 我没有意识到这一点,你的意图。因此,请随时回滚我的更改。或者只是在这一行添加一个 C 注释?
      【解决方案3】:

      正如其他人提到的,msg 的内存在循环开始(或结束)的那一刻被释放。

      除了在堆上分配之外,您还可以像这样定义一个“字符串”数组:

      int main(int argc, char *argv[]){
      
        pthread_t elevator_thread;
        pthread_t person_threads[MAX_PERSONS]; //My array of threads
        char msg[MAX_MSG_LEN + 1][MAX_PERSONS];
        char *elev_msg = "Elevator thread started";
      
        pthread_create(&elevator_thread, NULL, elevator, (void *) elev_msg);
      
        for (size_t i = 0; i < MAX_PERSONS; i++){
          snprintf(msg[i], sizeof msg, "Person thread %zu started", i);
          pthread_create(&person_threads[i], NULL, person, msg[i]);
        }
      
        ...
      

      或者甚至更好地将属于的东西放在一起:

      struct Person_Thread 
      {
        pthread_t thread;
        char msg[MAX_MSG_LEN + 1];
      };
      
      int main(int argc, char *argv[]){
      
        pthread_t elevator_thread;
        struct Person_Thread person_threads[MAX_PERSONS]; //My array of threads
        char *elev_msg = "Elevator thread started";
      
        pthread_create(&elevator_thread, NULL, elevator, (void *) elev_msg);
      
        for (size_t i = 0; i < MAX_PERSONS; i++){
          snprintf(person_threads[i].msg, sizeof person_threads[i].msg, 
            "Person thread %zu started", i);
      
          pthread_create(&person_threads[i].thread, NULL, person, 
            person_threads[i].msg]);
        }
      
        ...
      

      【讨论】:

      • 嗨@alk,你不一定正确地释放了味精内存。一些编译器会将 char msg[50] 提升到循环之外,这是一个完全可以接受的优化。最好的解决方法实际上是只传递循环变量而不是缓冲区,并构造消息以在具有本地存储的线程内输出。
      • @EdBayiates:好的,“解除分配”可能有点具体为什么变量的内存变得无效 每次迭代,它按照 C 标准执行,因为它的范围在每次迭代时都会离开(并重新进入)。
      猜你喜欢
      • 1970-01-01
      • 2017-09-08
      • 2012-03-28
      • 2019-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多