除了创建一个线程之外,访问另一个线程的返回值

时间:2017-08-08 06:27:48

标签: c multithreading concurrency

为什么copyfilepass返回指向callcopypass可以将此值作为args[2]访问时复制的字节数的指针?

#include <unistd.h>
#include "restart.h"

void *copyfilepass(void *arg)  {
   int *argint;

   argint = (int *)arg;
   /* copyfile copies from a descriptor to another */
   argint[2] = copyfile(argint[0], argint[1]);
   close(argint[0]);
   close(argint[1]);
   return argint + 2;
}

callcopypass.c

#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#define PERMS (S_IRUSR | S_IWUSR)
#define READ_FLAGS O_RDONLY
#define WRITE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC)
void *copyfilepass(void *arg);

int main (int argc, char *argv[]) {
   int *bytesptr;
   int error;
   int targs[3];
   pthread_t tid;

   if (argc != 3) {
      fprintf(stderr, "Usage: %s fromfile tofile\n", argv[0]);
      return 1;
   }

   if (((targs[0] = open(argv[1], READ_FLAGS)) == -1) ||
       ((targs[1] = open(argv[2], WRITE_FLAGS, PERMS)) == -1)) {
      perror("Failed to  open the files");
      return 1;
   }
   if (error = pthread_create(&tid, NULL, copyfilepass, targs)) {
      fprintf(stderr, "Failed to create thread: %s\n", strerror(error));
      return 1;
   }
   if (error = pthread_join(tid, (void **)&bytesptr)) {
      fprintf(stderr, "Failed to join thread: %s\n", strerror(error));
      return 1;
   }
   printf("Number of bytes copied: %d\n", *bytesptr);
   return 0;
}

作者回答说

  

如果创建线程以外的线程与copyfilepass连接,则为   可以访问通过参数复制的字节数   pthread_join

我甚至不理解答案。另一个线程如何访问返回值(即更改值?)而不是创建一个?如果可能的话,你能解释一下吗?

2 个答案:

答案 0 :(得分:2)

答案的关键是你可以预见到要从创建它的线程以外的线程读取copyfilepass线程的结果(在这种情况下是复制的字节数)。假设,为了示例,我们有第三个线程monitorcopy,而tid是全局而不是局部变量。 monitorcopy在主方法copyfilepass之后生成。

void* monitorcopy(void* params) {
  void *result
  pthread_join(tid, &result);
  /* Point A: Attempt to read result */
}

假设copyfilepass返回NULL或无意义的值。在A点,resultNULL,我们无法检索复制的字节数,因为它存储在main方法的targs[2]中,超出了范围。< / p>

假设copyfilepass返回argint + 2result现在是指向复制的字节数的指针,即使我们与targs的范围不同。因此,在没有任何内存生存期问题的情况下,我们可以按如下方式访问复制的字节数:

void* monitorcopy(void* params) {
  void *result
  pthread_join(tid, &result);
  int bytesCopied = *((int*) result);
}

答案 1 :(得分:2)

问题不在于另一个线程想要&#34;更改返回值&#34;,它是否有不同的线程可以访问输入参数(targs)。通常,pthread_join允许您从程序中的任何位置获取某个线程的结果值,只要您拥有线程ID即可。因此,使用此值返回异步操作的结果是不明智的?

然而,这个例子写得很差(作为良好的多线程实践的一个例子),原因有很多:

  1. 只有一个函数,所有变量的范围都延伸到main。这样写,无论如何每个人都可以访问输入参数。当你说在这种情况下通过pthread_join阅读结果时,你是对的。

  2. 将堆栈变量(targs)传递给线程是一个坏主意。当函数结束时,变量将超出范围,因此程序不会崩溃的唯一安全方法是立即加入线程,防止targs超出范围。这意味着你不会从多线程中获得任何好处(除非main在加入之前做了一些额外的工作)。它们应该是全局的,或者在堆上分配(malloc / free对)。

  3. 文件在main内打开,但在copyfilepass内关闭。这种责任转移是不必要的,尽管并不罕见。我会将文件名传递给函数并处理那里的开头,或者在复制文件后关闭线程外的句柄。

  4. 无论如何,代码作者的观点是,您不需要在您加入主题的地方访问输入参数:

    // removed all error checks for simplicity
    int main (int argc, char *argv[]) {
    
       pthread_t tid;    
    
       // removed all error checks for simplicity
       pthread_create(&tid, NULL, copy_file, argv);
    
       // note that this function only accepts the thread id
       wait_until_copied(tid);
    
       return 0;
    }
    
    void wait_until_copied(pthread_t tid)
    {
        int *bytesptr;
        // no way to access input args here
        pthread_join(tid, (void **)&bytesptr);
        printf("Number of bytes copied: %d\n", *bytesptr);
    }