基本 posix 线程参数传递问题

Basic posix thread argument passing issue

我正在尝试进入线程世界,但遇到了一些麻烦。下面的代码偶尔会起作用,但似乎是完全随机的。给它相同的输入总是给我不同的结果,我很困惑。 有时 PrintHello() 会打印出参数,有时会打印出垃圾,有时只会出现段错误。

#define NUM_THREADS 5
char *prompt = "% ";

struct thread_data{
    int thread_id;
    //int sum;
    char *message;
};

void *PrintHello(void *threadarg)
{   
   struct thread_data *local_data;
   local_data = (struct thread_data *) threadarg;
   int taskid = local_data->thread_id;
   const char *arguments = local_data->message;
   fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
   pthread_exit(NULL);
}

PrintHello() 是我认为的问题所在。

int main()
{
    int pid;
    //int child_pid;
    char line[81];
    char *token;
    char *separator = " \t\n";
    char **args;
    char **args2;
    char *hp;
    char *cp;
    char *ofile;
    int i;
    int j, h, t, rc;

    args = malloc(80 * sizeof(char *));
    args2 = malloc(80 * sizeof(char *));

    signal(SIGINT, SIG_IGN);

    while (1) {
        fprintf(stderr, "%s", prompt);
        fflush(stderr);

        if (fgets(line, 80, stdin) == NULL)
            break;

       /* get rid of the '\n' from fgets */
        if (line[strlen(line) - 1] == '\n'){
               line[strlen(line) - 1] = '[=12=]';
    }
    // split up the line

    i = 0;
    while (1) {
        token = strtok((i == 0) ? line : NULL, separator);
        if (token == NULL)
            break;
        args[i++] = token;
    }

    args[i] = NULL;
    ofile = args[i-1];
    printf("%s\n", ofile);

上面的内容只是将输入标记化并且工作正常。

    struct thread_data thread_data_array[i];
    pthread_t threads[i];



    for(t=0; t<i; t++){

        thread_data_array[t].thread_id = t;
        thread_data_array[t].message = args[t];

        rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
        if (rc){
            printf("ERROR; return code from pthread_create() is %d\n", rc);
            exit(-1);
        }
  }
}      
}

您的代码存在多个问题,但我将重点关注影响稳定性的关键问题。

  1. 您没有检查 malloc() 的 return 值。如果 return 值为 NULL,则表示操作失败,您必须重试,或者开始清理 malloc()calloc()、[=15 中的所有动态分配内存=], 等等,优雅地完成你的程序。尝试取消引用 NULL(即:使用来自失败的内存分配调用的指针)将使您的程序崩溃。
  2. 您的程序不考虑提供的零个有效参数(即:只需在提示符下点击 ENTER。更改 ALL 换行实例到 '[=16=]',然后继续。
  3. 此外,计算您发现的令牌数量。好的做法,并帮助您检查是否未找到有效输入。
  4. 考虑阅读 starting threads in a detached state versus a joinable state。您的代码中最大的问题是您启动了所有线程,然后您的 while 循环立即再次执行,并将新值重新分配给 thread[] 数组。此外,相同的参数在仍在使用时传递给它们(thread_data_array[t]),并且您没有互斥锁来保护它们。此外,如果您的程序 main() 提前退出,那么所有 运行 线程都会立即被杀死,并且不会完成。
  5. 您应该 pthread_join() 在可加入的线程上,以确保您等到它们完成后再继续。
  6. 您没有提供不使用 CTRL+C 或使程序崩溃的方法来退出程序。不是个好主意。
  7. 请注意,线程不一定按照您创建它们的顺序执行,但在本例中幸运的是它们执行了。您将需要了解屏障、条件变量 (condvars) 和互斥量以进行更高级的同步处理。
  8. 您丢失了很多重要的头文件。对你的代码编译感到惊讶。
  9. Learn how to debug your code with gdb. In this case, I compiled it via gcc test.c -lpthread -O0 -ggdb, then stepped through the code via the "next" n command after starting it in gdb with run. It makes your life a lot easier.

更新代码清单


#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

#define NUM_THREADS 5
#define BUF_LEN (80)
char *prompt = "% ";

struct thread_data{
    int thread_id;
    //int sum;
    char *message;
};

void *PrintHello(void *threadarg)
{   
    struct thread_data *local_data;
    local_data = (struct thread_data *) threadarg;
    int taskid = local_data->thread_id;
    const char *arguments = local_data->message;
    fprintf(stderr, "Hello World! It's me, thread %s!\n", arguments);
    pthread_exit(NULL);
}

int main()
{
    int pid;
    //int child_pid;
    char line[81];
    char *token;
    char *separator = " \t\n";
    char **args;
    char **args2;
    char *hp;
    char *cp;
    char *ofile;
    int i;
    int j, h, t, rc;

    args = malloc(BUF_LEN * sizeof(char *));    // ISSUE: Can fail. Check return value.
    args2 = malloc(BUF_LEN * sizeof(char *));   // ISSUE: Can fail.

    signal(SIGINT, SIG_IGN);

    while (1)
    {
        fprintf(stderr, "%s", prompt);
        fflush(stderr);

        if (fgets(line, BUF_LEN, stdin) == NULL)
        {
            break;
        }

        /* get rid of the '\n' from fgets */
        /*
        if (line[strlen(line) - 1] == '\n'){
            line[strlen(line) - 1] = '[=10=]';
        }
        */
        for ( t = 0; t < BUF_LEN; t++ )
        {
            if ( line[t] == '\n' )
            {
                line[t] = '[=10=]';
            }
        }

        // split up the line
        i = 0;
        int numTokens = 0;
        while (1) {
            token = strtok((i == 0) ? line : NULL, separator);
            if (token == NULL)
            {
                break;
            }
            args[i++] = token;
            numTokens++;
        }
        // Abort if zero tokens found.
        if ( numTokens == 0 )
        {
            continue;
        }
        // Exit if input is "quit"
        if ( strcasecmp(line, "quit") == 0 )
        {
            break;
        }

        args[i] = NULL;
        ofile = args[i-1];
        printf("%s\n", ofile);
        struct thread_data thread_data_array[i];
        pthread_t threads[i];

        for(t=0; t<i; t++)
        {
            thread_data_array[t].thread_id = t;
            thread_data_array[t].message = args[t];

            rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_data_array[t]);
            if (rc)
            {
                printf("ERROR; return code from pthread_create() is %d\n", rc);
                exit(-1);
            }
        }

        // Wait for threads to complete work.
        for(t=0; t<i; t++)
        {
            pthread_join(threads[t], NULL);
        }
    }      
}

样本运行


% 
% 
% 
% Hello world. This is a test
test
Hello World! It's me, thread test!
Hello World! It's me, thread a!
Hello World! It's me, thread is!
Hello World! It's me, thread This!
Hello World! It's me, thread world.!
Hello World! It's me, thread Hello!
% 
% 1 2 3
3
Hello World! It's me, thread 3!
Hello World! It's me, thread 2!
Hello World! It's me, thread 1!
% QUit