【问题标题】:pthreads are not executing seriallypthreads 没有串行执行
【发布时间】:2012-08-11 16:17:44
【问题描述】:

我是这些 pthread 的新手。我编写了一个程序,以便按顺序显示,而不是随机显示数字。我为此使用了 pthrea_join() 方法。程序如下:

int cnt=0,i=1;
pthread_t th[10];
int printmsg()
{
  cnt++;
  printf( "thread no. %d\n",cnt);
}
void tosync()
{
  if(i>0)
  pthread_join(th[i-1],NULL); // <---here i am blocking the previous thread..                         
  printmsg();
}
void main(void)
{
  pthread_create(&th[0], NULL,(void*)&tosync, NULL);
  for( i=1;i<10; i++){
    pthread_create(&th[i],NULL, (void*) &tosync, NULL);
  } 
  int y;
  for(int i=0; i<10; i++)
    pthread_join(th[i],NULL);
  return;
}

我仍然随机获取数字... 请。帮助

【问题讨论】:

  • 访问全局变量i时好像有问题。来自主线程(增量)以及来自工作线程。除此之外,您希望th[0] 做什么?等待自己?
  • 对不起,我忘了编辑...即使现在我还是随机获取数字

标签: c synchronization pthreads


【解决方案1】:

您应该将一些内容传递给您的tosync 例程,以便每个线程都知道它应该等待哪个线程。此外,第一个线程不应该等待任何人。如果您对线程启动例程使用正确的签名,则可以传入一个参数。

void * tosync(void *arg)
{
  pthread_t *me = (pthread_t *)arg;
  if (me > th) pthread_join(me[-1],NULL);
  printmsg();
  return 0;
}

main 应该返回 int。它的循环现在被简化了,因为启动例程不再需要强制转换。由于每个线程都已经加入了它的前任,main 线程只需要加入最后一个线程。

int main(void)
{
  for( i=0;i<10; i++){
    pthread_create(&th[i],NULL, tosync, &th[i]);
  }
  pthread_join(th[9],NULL);
  return 0;
}

【讨论】:

    【解决方案2】:

    pthread_join(th[i-1],NULL) 这一行有一些问题。当你创建一个线程时,你会创建 i 的值。认为 创建前三个线程操作系统切换第三线程启动和操作系统切换到它创建其余线程的主线程。创建所有线程后 i 的值为 10/ 现在假设操作系统切换到第三线程然后他等待 10-1 = 9th 线程完成并类似地继续。所以最终它总是随机打印。你的策略是错误的。

    试试这个

    int cnt=0,i=1;
    pthread_t th[10];
    int printmsg()
    {
    cnt++;
    printf( thread no. %d\n",cnt);
    }
    void tosync()
    {
    
    printmsg();
    }
    void main(void)
    {
    pthread_create(&th[0], NULL,(void*)&tosync, NULL);
    for( i=1;i<10; i++){
    pthread_create(&th[i],NULL, (void*) &tosync, NULL);
    pthread_join(th[i],NULL); // <---here i am blocking the previous thread..   
    } 
    
    return;
    }
    

    【讨论】:

      【解决方案3】:

      另一种方法:

      #include <stdio.h>
      #include <pthread.h>
      
      struct threadData{
          int id;
          pthread_t prev;
      };
      
      
      void tosync(void *data)
      {
          struct threadData *td=data;
          if ((*td).prev!=0){
              printf("%i waiting\n",(*td).id);
              fflush(0);
              pthread_join((*td).prev,NULL); // <---here i am blocking the previous thread..                         
          }
          printf("%i runnning\n",(*td).id);
          fflush(0);
          free(td);
      }
      
      int main(void)
      {
          int i;
          struct threadData *td;
          pthread_t nextThreadID=0;
          for( i=0;i<10; i++){
              td=malloc(sizeof(struct threadData));
              (*td).prev=nextThreadID;
              (*td).id=i;
              pthread_create(&nextThreadID,NULL, (void*) &tosync, (void *)td);
          } 
          pthread_join(nextThreadID,NULL);
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        toSync 函数中,您使用的是i 值,问题是当toSync 函数运行时您不知道i 具有什么值。

        在极端情况下,所有线程的值可能为 10,如果创建线程的循环在任何创建的线程可以运行之前运行,就会发生这种情况。

        干净的解决方案是将i 值作为参数传递给pthread_create,并让toSync 使用它而不是全局i。例如

        int *id = (int*)malloc(sizeof(int));
        *id = i;
        pthread_create(&th[i],NULL, (void*) &tosync, id);
        

        其他需要考虑的事情:

        1. toSync 需要对 id 为 0 的线程进行特殊处理,因为它没有任何前任等待
        2. main 中的最后一个循环不应在线程 0-8 上调用 pthread_join,因为它们已经加入。在同一个线程上多次调用pthread_join的结果是undefined

        【讨论】:

        • 为此使用malloc 会严重浪费内存和cpu 时间。只需将i 转换为void * 而不是获取其地址,然后在线程启动函数中将其转换回int
        • 另请注意,这是浪费同步:在一个线程中调用malloc,在另一个线程中调用free,总会有一些隐藏的同步成本,获取内存并将其返回到空闲池以供重用。跨度>
        • 是的。如果只需要 id 来跟踪前任线程,我投票支持@Joachim Isaksson 提供的解决方案 - 只需传递线程的地址。
        • @R.. 啊,不要教初学者这样的东西。这里有一个案例,有人很难理解线程之间的基本同步和类似的东西,你告诉他违反接口?
        • 不违反接口。 pthread_create 和线程启动函数采用void * 类型的。接口契约不要求这个值是指向任何东西的有效指针。 void *int 之间的转换是实现定义的,但每个现实世界的实现都以一种有意义的方式定义它,这样您就可以转换至少小的整数(除了非常奇特的拱门,all i> 适合int) 从intvoid * 的整数,并返回而不会丢失。
        【解决方案5】:

        发生的情况是您的tosync() 方法使用全局变量i,而不是在线程实际启动时知道i 的值。如果您想将i(或此处指向前一个pthread_t 的指针)传递给tosync,以便它真正记住它应该加入哪个线程,您需要通过pthread_create 传递它,类似于;

        void* tosync(void* ptr)
        {
          pthread_t* threadIndex = (pthread_t*)ptr;
          if(threadIndex != NULL)
              pthread_join(*threadIndex, NULL); // <---here i am blocking the previous thread..                         
          printmsg();
        }
        
        ...in the loop...
        
        pthread_create(&th[i], NULL, tosync, &th[i-1]);
        

        它是 pthread_create 的最后一个参数,将在您的方法中作为 threadIndex 传入,如果其中包含索引,则每个线程都会知道其单独的索引。

        【讨论】:

        • 很好,你只需要小心第一个线程,所以pthread_create 调用和tosync 肯定包含一些条件。
        • @JensGustedt 好点。检查 NULL 并让第一个线程继续将 NULL 作为参数传递应该就足​​够了。修复了代码:)
        猜你喜欢
        • 2013-12-17
        • 2023-03-29
        • 2013-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-28
        • 2014-06-14
        • 1970-01-01
        相关资源
        最近更新 更多