【问题标题】:Basic posix thread argument passing issue基本 posix 线程参数传递问题
【发布时间】:2016-03-30 01:07:54
【问题描述】:

我正在尝试进入线程世界,但遇到了一些麻烦。下面的代码每隔一段时间就会运行一次,但似乎是完全随机的。给它相同的输入总是给我不同的结果,我很困惑。 有时 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] = '\0';
    }
    // 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);
        }
  }
}      
}

【问题讨论】:

  • 您需要主线程等待所有子线程退出后才退出。当主线程退出时,所有子线程也会被杀死。在主线程中为每个子线程调用pthread_join
  • @kaylum 哇,好用! pthread_join 似乎已经解决了所有问题,谢谢。
  • 如果线程在main 返回后启动,您期望会发生什么?然后参数指向一个已被销毁的对象。

标签: c multithreading arguments posix


【解决方案1】:

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

  1. 您没有检查malloc() 的返回值。如果返回值为 NULL,则表示操作失败,您必须重试,或者开始清理来自malloc()calloc()strdup() 等的所有动态分配的内存,并完成您的优雅地编程。尝试取消引用 NULL(即:使用来自失败的内存分配调用的指针)将使您的程序崩溃。
  2. 您的程序不考虑提供的零有效参数(即:只需在提示符处按 ENTER。将 ALL 换行实例更改为 '\0',然后继续。
  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. 请注意,线程不一定按照您创建它们的顺序执行,尽管幸运的是它们在这种情况下执行了。您需要了解障碍、条件变量 (condvar) 和互斥锁,才能进行更高级的同步处理。
  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] = '\0';
        }
        */
        for ( t = 0; t < BUF_LEN; t++ )
        {
            if ( line[t] == '\n' )
            {
                line[t] = '\0';
            }
        }

        // 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

【讨论】:

  • 哇,谢谢!我不知道我怎么从来没有听说过 GDB,它改变了游戏规则。我一直在移动 printf 以尝试找出问题所在。我对头文件表示歉意。我确实有很多标题,但我删除了它们,因为当我发布太多代码时,人们会在这里对我生气。我本来想提的,但我忘了。
  • @Pongjazzle 没有问题。如果这解决了您的问题,请考虑通过单击 upvote/downvote 按钮附近的复选标记按钮将其标记为“已接受”。将来,多余的代码不是问题。提供一个工作示例可以节省我们的时间。 How to create a Minimal, Complete, and Verifiable example.
  • @Pongjazzle 是的,GDB 非常有用。当程序崩溃并且您不知道原因时,这是您的第一个首选工具。 Also look into valgrind, which helps with diagnosing thread/locking and memory leak bugs.。 GDB 和 Valgrind 是任何认真的 Linux 开发人员都必须知道的两个工具,添加到您的简历中令人印象深刻。祝你好运!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多