【问题标题】:Passing multiple arguments to threaded function from pthread_create从 pthread_create 将多个参数传递给线程函数
【发布时间】:2013-04-20 06:42:52
【问题描述】:

这是我第一次尝试在 C 中进行线程化。我正在创建一个循环有界缓冲区。我知道如何创建线程,但我看到的所有示例都只有接受一个 void 参数的线程函数,但不幸的是我的工人规范要求我使用三个,如下所示:

void bufferRead(BoundedBuffer* buffer, char* data, int count) {
    pthread_mutex_lock(&buffer->mutexBuffer);
    <snip>
    pthread_mutex_unlock(&buffer->mutexBuffer);
}

这是我的 pthread_create 语句

pthread_create(&buffer.readThread, NULL, (void *)bufferRead, &readParams)

还有我的 readParams 结构/赋值

struct readThreadParams {                                                   
    BoundedBuffer b;                                                        
    char* data;                                                             
    int count;                                                              
};                                                                          

struct readThreadParams readParams;                                         
readParams.b = buffer2;                                                     
readParams.data = out_array;                                                
readParams.count = in_size;

任何关于在传递给 bufferRead 函数后如何分配每个结构参数的建议将不胜感激。

【问题讨论】:

    标签: c pthreads posix


    【解决方案1】:

    那是因为你真正需要一个参数。当我们有多个值时,通常情况下,我们将其封装到一个结构中。 pthread_create 将调用的函数类型是不可协商的。这是一个对函数指针进行类型转换可能会给您带来严重麻烦的领域。

    #include <pthread.h>
    #include <stdlib.h>
    
    struct BoundedBuffer {
        pthread_t readThread;
        pthread_mutex_t mutexBuffer;
    } buffer2;
    
    struct readThreadParams {
        struct BoundedBuffer b;
        char* data;
        int count;
    };
    
    void *bufferRead (void *context) {
        struct readThreadParams *readParams = context;
    
        pthread_mutex_lock(&readParams->b.mutexBuffer);
        //<snip>
        pthread_mutex_unlock(&readParams->b.mutexBuffer);
    
        return NULL;
    }
    
    int main(void) {
        int ret;
        char *out_array = malloc(42);
        size_t in_size = 42;
    
        struct readThreadParams readParams;
        readParams.b = buffer2;
        readParams.data = out_array;
        readParams.count = in_size;
    
        /* I presume that by "buffer", you really meant the .b member of
         * struct readThreadParams.  Further, this must have a member
         * named readThread of type pthread_t, etc.
         */
        ret = pthread_create(&readParams.b.readThread, NULL, bufferRead, &readParams);
    
        if (!ret) {
            pthread_join(&readParams.b.readThread, NULL);
        }
    
        free(out_array);
    
        return ret;
    }
    

    【讨论】:

    • 如果它是 main 中的局部变量,那不是问题。如果您退出 main,那么您的程序无论如何都会终止,因此该变量在您的程序运行时始终存在。
    • @jcoder 离开main 并不意味着程序立即终止。而用于线程间通信的自动(非堆、非静态)变量充其量是一个值得商榷的想法。
    • 好的,你是对的,离开 main 和执行退出之间有一个很小的间隔。所以不安全。我同意。我打算写一些关于只要你在传入的局部变量之后创建线程对象就可以保证安全的东西,但这是 c/pthreads,而不是我最初认为的 c++。
    • OP 代码没有提到 main()。在非平凡的应用程序的一般情况下,使用本地堆栈结构作为参数是非常危险的。
    • @Valeri Atamaniouk 这个练习的重点是演示创建线程的基本机制。您会注意到在您退出之前我有一条关于加入线程的评论,这将防止该问题。我已经将其替换为实际加入的代码。将堆栈上的数据传递给线程本身并不是一件坏事——它取决于上下文。在这种情况下,我不在乎我的主线程必须阻塞,直到工作线程完成。
    【解决方案2】:

    这个例子接近于预处理器滥用,但我喜欢它,因为它演示了模仿默认参数值。

    #include <assert.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef char *BoundedBuffer;
    
    struct read_thread_param {  
        pthread_t thread;                                                 
        BoundedBuffer buffer;                                                        
        char* data;                                                             
        int count;                                                              
    }; 
    
    void buffer_read(BoundedBuffer* buffer, char* data, int count) {
        pthread_mutex_lock(&buffer->mutexBuffer);
        /*snip*/
        pthread_mutex_unlock(&buffer->mutexBuffer);
    }
    
    void *buffer_read_entrance(void *object) {
        struct read_thread_param *param = object;
        if (param->thread != 0) {
            buffer_read(&param->buffer, param->data, param->count);
            free(param);
            return NULL;
        }
    
        param = malloc(sizeof *param);
    
        /* TODO: Handle allocation error */
        assert(param != NULL);
    
        memcpy(param, object, sizeof *param);
    
        /* TODO: Handle thread creation error */
        assert(pthread_create(&param->thread, NULL, buffer_read_entrance, param) == 0);
        return NULL;
    }
    
    #define buffer_read_entrance(...) buffer_read_entrance(&(struct read_thread_param) { .thread = 0, __VA_ARGS__ })
    void buffer_read(BoundedBuffer* buffer, char* data, int count);
    
    int main(void) {
        buffer_read_entrance(.buffer = "hello world", .count = 42);
    }
    

    【讨论】:

      【解决方案3】:

      启动函数必须接受参数。所以你的方向是对的:

      struct readThreadParams {                                                   
          BoundedBuffer *b; 
          char *data;                                                             
          int count;                                                              
      };     
      

      然后,您需要在堆上分配变量,而不是在堆栈上:

      struct readThreadParams *readParams;
      
      readParams = malloc(sizeof(*readParams));
      readParams->b = buffer2;                                                     
      readParams->data = out_array;                                                
      readParams->count = in_size;
      

      之后你可以给它createThread:

      pthread_create(&buffer.readThread, NULL, bufferRead, readParams);
      

      线程函数只需要1个参数(void*):

      void *bufferRead(void *arg)
      {
          struct readThreadParams *params = arg;
          BoundedBuffer *buffer = params->b;
          char* data = params->data;
          int count = params->count;
      
          pthread_mutex_lock(&buffer->mutexBuffer);
          <snip>
          pthread_mutex_unlock(&buffer->mutexBuffer);
      
          return NULL;
      }
      

      【讨论】:

        【解决方案4】:

        你的方法是对的。

        函数原型应该是这样的

        void*  bufferRead(void *arg)
        {
           ....
        }
        

        并将参数类型转换为线程函数中所需的类型。这里应该是

        void*  bufferRead(void *arg)
        {
             struct readThreadParams *input = (struct readThreadParams*)arg;
        }
        

        直接向 pthread 函数传递多个参数是不可能的。所以大多形成结构并传递给函数。

        有关 pthread 的更多详细信息,请参阅 this 教程。

        【讨论】:

        • 感谢您的快速回复!只是想知道,但有什么办法仍然可以使用我目前拥有的函数原型?
        • 您可以进行类型转换和创建。但是没有用。所以推荐void * ()(void*)
        • @ChrisDevWard 不,你不能。好吧,使用该原型的唯一方法是直接从 pthread 库将调用的 void *start_routine(void *) 函数中调用它,但这会不必要地引入另一个函数调用的开销。
        • @Jeyaram 另外,从 void* 到另一个指针类型的类型转换并不是严格要求的(我不确定这可能需要什么 C 标准),但是从另一个指针类型到void* 确实需要显式类型转换。
        • @DanielSantos 来自void * 的类型转换可以转换为任何指向对象类型的指针,然后再转换回来,值应转换为相等。这是所有C标准,AFAIK。如果您尝试访问它指向的对象,并且指针未适当对齐以存储该类型,或者它未指向合适大小或表示形式的对象,则存在未定义行为的方面。
        猜你喜欢
        • 1970-01-01
        • 2023-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-05
        • 2022-08-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多