【问题标题】:pthread void pointer castingpthread void 指针转换
【发布时间】:2018-10-15 20:23:10
【问题描述】:

我正在处理一项任务,该任务要求我使用线程来处理和同步从文件中获取数据。我的教授告诉我,我可以将数据更改为 void 指针以将其传递给我的函数,然后再将其转换回。我正在尝试使用文件 IO 来做到这一点。

pthread_create(&th1, NULL, processing, (void *)&fp);

在我的处理函数中,我试图将其转换回 FILE 指针:

FILE driveOne = (FILE *)file;

这显然行不通,谁能给我解释一下?

【问题讨论】:

  • 1) 将(void *) 转换为(FILE *) 应该可以工作。 2) 你确定要&fp,而不是(void *)fp? 3)另外:应该是FILE *driveOne = (FILE *)file
  • 如果您像这样声明pthread_create,您甚至不需要在调用fp 时将其转换为:FILE *fp;

标签: c multithreading pointers void


【解决方案1】:

这里有一个更完整的例子。

假设您的工作函数需要一个文件句柄。为简单起见,假设它从中读取每个字符,并返回读取的字符数,然后转换为指针:

void *worker(void *data)
{
    FILE      *handle = (FILE *)data;
    uintptr_t  count = 0;

    if (handle && !ferror(handle)) {
        /* handle is a valid file handle */

        while (getc(handle) != EOF)
            count++;
    }

    return (void *)count;
}

如果count 属于intptr_tuintptr_t 以外的其他类型(在<stdint.h> 中声明,通常包含<inttypes.h>),您需要先将其转换为该类型,然后空指针,即(void *)(uintptr_t)count

因为这样的工作线程不需要太多堆栈(准确地说几乎没有),而且默认线程堆栈大小很大(兆字节),我们可以节省一些内存(如果需要,可以允许更多线程,尤其是在 32位架构)通过创建一个 pthread 属性来指示pthread_create() 使用较小的堆栈。该属性不被调用“消耗”;它更像是一个配置块。

假设您有三个流FILE *in[3];,并且您希望使用三个线程来检查它们的长度。使用 pthread 属性来使用较小的堆栈(2*PTHREAD_STACK_MIN,定义在 <limits.h> 中,对于不使用 alloca() 或本地数组的工作线程来说是一个很好的安全值。):

pthread_t       worker_id[3];
uintptr_t       length[3];
pthread_attr_t  attrs;
void           *retptr;
int             i, result;

/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);

/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
    result = pthread_create(&(worker_id[i]), &attrs, worker, (void *)in[i]);
    if (result) {
        fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
}

/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);

/*
  ... This thread can do something else here ...
*/

/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
    result = pthread_join(worker_id[i], &retptr);
    if (result) {
        fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
    length[i] = (uintptr_t)retptr;
}

for (i = 0; i < 3; i++)
    printf("in[%d] contained %llu chars.\n", i, (unsigned long long)length[i]);

当您想将多个参数传递给线程函数时,可以使用相同的模式。您首先构建一个结构来保存这些参数,然后创建它们。您可以动态分配它们,将它们声明为全局变量,或在 main() 中将它们声明为局部变量——在工作线程存在的整个持续时间内存在的任何作用域都可以工作。

例如,假设您的工作函数计算从流中读取的每个 unsigned char 值的直方图:

struct work {
    pthread_t     id;                   /* Thread identifier */
    FILE         *in;                   /* File handle to read from */
    size_t        count[UCHAR_MAX + 1]; /* Histogram */
};

void *worker(void *data) {
    struct work *const  work = (struct worker_data *)data;
    int                 c;

    if (!work || !work->in) {
        /* Invalid data, or invalid file handle. */
        return (void *)(intptr_t)(EINVAL);
    }
    if (ferror(work->in)) {
        /* Stream is in error state. */
        return (void *)(intptr_t)(EIO);
    }

    /* Read the stream. */
    while ((c = getc(work->in)) != EOF) {
        /* Update histogram. */
        work->count[(unsigned char)c]++;
    }

    /* Did the reading stop due to an I/O error? */
    if (ferror(work->in))
        return (void *)(intptr_t)(EIO);

    /* No errors, all done. */
    return (void *)0;
}

注意struct work *const work = ... 初始化一个常量指针work,而不是一个指向常量的指针。 const 只是一个优化告诉 C 编译器我们不会尝试修改 work 指针本身。它指向的数据是可修改的。

(要阅读指针声明,请从右到左阅读它们,将每个* 替换为“是指向”的指针,以获得正确的含义。)

创建这些worker的代码非常相似,只是我们动态分配工作:

struct work    *work[3];
pthread_attr_t  attrs;
void           *retptr;
int             i, result;

/* Create and initialize the three pointers. */
for (i = 0; i < 3; i++) {

    /* Allocate a work structure. */
    work[i] = malloc(sizeof *(work[i]));
    if (!work[i]) {
        fprintf(stderr, "Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    /* Copy the handle to read from, */
    work[i]->in = in[i];

    /* and clear the histogram part. */
    memset(work[i]->count, 0, sizeof work[i]->count);
}

/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);

/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
    result = pthread_create(&(work[i]->id), &attrs, worker, (void *)work[i]);
    if (result) {
        fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
}

/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);

/*
  ... This thread can do something else here ...
*/

/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
    result = pthread_join(work[i]->id, &retptr);
    if (result) {
        fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    /* If the thread reported a failure, print the corresponding
       error message (but do not exit). */
    if (retptr)
        fprintf(stderr, "Thread %d of 3: %s.\n", i+1, strerror((intptr_t)retptr));

    /* ... print the histogram here? ... */
}

/* Free the work structures. */
for (i = 0; i < 3; i++)
    free(work[i]);

如果您不想在发生错误时中止程序,请注意free(NULL) 是安全的并且什么也不做; struct work *pointerarray[SIZE] = {0}; 声明了一个指向 struct work 的 SIZE 指针数组,并将它们全部初始化为零。例如,如果分配或线程创建在某个时候失败,您可以只free() 每个指针,无论其分配是否成功。

也就是说,如果你想分配三种不同类型的结构(struct atype *a;struct btype *b;struct ctype *c;),你可以这样做

a = malloc(sizeof *a);
b = malloc(sizeof *b);
c = malloc(sizeof *c);
if (!a || !b || !c) {
    free(c);
    free(b);
    free(a);
    return ALLOCATION_FAILED;
}

/* Allocation was successful */

而不是分配每一个并分别测试失败。

【讨论】:

    【解决方案2】:

    您需要将driveOne 声明为FILE *,而不是FILE

    FILE *driveOne = (FILE *)file;
    

    此外,假设 fp 最初声明为 FILE *,您对 pthread_create 的调用不应在 fp 之前有 &,如下所示:

    pthread_create(&th1, NULL, processing, (void *)fp);
    

    【讨论】:

    • 声明FILE fp总是是错误的。您无法有意义地创建 FILE 类型的对象;在 C11 及更高版本中,它甚至不一定是完整类型。
    • fppthread_create() 中的演员表并不重要;该函数的原型本质上是自动完成的。同上,线程函数中的强制转换不是绝对必要的。 C 会自动从 void * 转换为其他对象指针类型(与 C++ 不同)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-22
    • 2012-11-02
    • 2015-02-14
    • 2016-01-12
    • 1970-01-01
    • 2011-07-31
    • 1970-01-01
    相关资源
    最近更新 更多