【问题标题】:Multithreaded search in cc中的多线程搜索
【发布时间】:2016-03-07 18:22:27
【问题描述】:

我应该有两个线程来搜索数组中的最小元素:第一个线程搜索前半部分,第二个线程搜索另一半。但是,当我运行我的代码时,它似乎随机选择了一个线程。我不确定我做错了什么,但这可能与“中间”部分有关。我尝试将一个数组分成两部分,找到中点,然后从那里写入条件,但我可能在某个地方出错了。我也尝试将 array[i] 放在条件中,但在这种情况下,只有 thread2 执行。

编辑:我真的在这里尽了最大的努力,但我没有得到任何地方。我以一种对我有意义的方式编辑了代码,我可能将“min”类型转换错了,但现在它甚至没有执行它只是给了我一个错误,即使它编译得很好。我只是一个初学者,虽然我理解你们所说的一切,但我很难实现这些想法,所以真的,任何解决这个问题的帮助都非常感谢!

EDIT2:好的,所以前面的代码根本没有意义,我很抱歉,但我在写它的时候已经筋疲力尽了。无论如何,我想出了其他部分有效的方法!我将数组分成两半,但是使用指针时只能访问第一个元素。但是,如果整个数组都被访问,它会起作用吗?如果是,我该如何解决呢?

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

#define size 20

void *smallest(void *arg);
pthread_t th, th2;
int array[size], i, min;

int main(int argc, char *argv[]) {

srand ( time(NULL) );
      for(i = 0; i < size; i++)
      {
              array[i] = (rand() % 100)+1;
              printf("%d ", array[i]);
   }

   int *array1 = malloc(10 * sizeof(int));
   int *array2 = malloc(10 * sizeof(int));
   memcpy(array1, array, 10 * sizeof(int));
   memcpy(array2, array + 10, 10 * sizeof(int));

    printf("\nFirst half gives %d \n", *array1);
    printf("Second half gives %d \n", *array2);


    pthread_create(&th, NULL, smallest, (void*) array1);
    pthread_create(&th2, NULL, smallest, (void*) array2);

    pthread_join(th, NULL);
    pthread_join(th2, NULL);

    //printf("\nFirst half gives %d\n", array1);
    //printf("Second half gives %d\n", array2);


if (*array1 < *array2) {
    printf("\nThread1 finds the number\n");
    printf("The smallest element is %i\n", *array1);
}
else {
    printf("\nThread2 finds the number\n");
    printf("The smallest element is %i\n", *array2);
}

return 0;
}

void *smallest(void* arg){

int *array = (int*)arg;

min = array[0];
for (i = 0; i < size; i++) {
  if (array[i] < min) {
     min = array[i];
  }
}
pthread_exit(NULL);
}

【问题讨论】:

  • 为什么不通过 pthread_create 将低和高作为线程参数传递?您的代码有两个线程在数组上运行。此外,两个线程都将它们的结果保存到同一个全局,这可能不是您想要的。
  • 我不明白这段代码如何实现问题中的要求?只能创建一个额外的线程 + 很多其他问题:(
  • OpenMP 比它简单 10 倍,并且受到 GCC 的支持,那么为什么要低级使用 pthread?
  • 你给pthread_create打了多少次电话?你应该创建多少个线程?
  • “我以一种对我有意义的方式编辑了代码” 创建 20 个线程对你有意义吗?编辑只是产生了原始问题中没有的新问题。

标签: c arrays multithreading


【解决方案1】:

您设置的代码永远不会运行多个线程。注意,如果你运行 if 语句的第一个分支,你会触发一个线程来搜索数组的一半,等待它完成,然后继续前进,如果 else 分支执行,同样的事情会发生在后半部分数组。从根本上说,您可能希望重新考虑您的策略,让代码始终启动两个线程并仅在两个线程开始运行后才加入其中。

您的 if 语句中的条件也似乎是错误的。您在询问数组的中间元素是否大于其索引。我想这不是你想要做的。

最后,您在每个线程中的代码总是查看整个数组,而不仅仅是其中的一半。我建议重写线程例程,使其参数表示取最小值的范围的开始和结束索引。然后,您将更新 main 中的代码,以便在启动线程时指定要搜索的范围。

我会这样安排:

  1. 触发一个线程来查找数组前半部分的最小值。
  2. 触发一个线程来查找数组后半部分的最小值。
  3. 加入两个线程。
  4. 使用每个线程的结果找出最小值。

最后一点,由于您将同时运行两个不同的线程,因此您需要注意数据竞争,因为两个线程都尝试读取或写入最小值。考虑让每个线程使用其退出代码来指示最小值在哪里,然后在 main 中解析真正的最小值。这消除了竞争条件。或者,有一个全局最小值,但用互斥锁保护它。

【讨论】:

    【解决方案2】:

    1) 你在 main 函数中重新声明了全局变量,所以实际上没有必要声明 i、low、high、min:

    int 数组[大小], i, low, high, min;

    当您在主函数中重新声明变量时,您遇到的问题是变量的范围,具有相同名称的全局变量变得“不可见”

       int *low = array;
       int *high = array + (size/2);
    
       int mid = (*low + *high) / 2;
    

    因此,当您运行线程时,变量的所有值(低、高、最小值; ) 为 0,这是因为它们实际上从未被 main 修改,并且因为它们以 0 默认值开始(启动代码等)。 无论如何,我不会真的推荐(它真的不赞成)使用全局变量,除非它是一个非常小的供个人使用的项目。

    2) 另一个关键问题是您忽略了线程背后的主要思想,即同时运行两个线程

     if (array[mid] > mid) {
        pthread_create(&th, NULL, &smallest, NULL);
        pthread_join(th, NULL);
        printf("\nThread1 finds the number\n");
      }
      else if (array[mid] < mid) {
        pthread_create(&th2, NULL, &smallest, NULL);
        pthread_join(th2, NULL);
        printf("\nThread2 finds the number\n");
      }
    

    执行时实际上只运行一个线程。 试试这样的:

    pthread_create(&th, NULL, &smallest, NULL);
    pthread_create(&th2, NULL, &smallest, NULL);
    pthread_join(th2, NULL);
    pthread_join(th, NULL);
    

    3) 您试图让两个线程访问同一个变量,这可能会导致未定义的行为,您必须使用互斥体以避免实际未存储数字。

    guide 关于互斥锁非常完整,但如果您需要任何帮助,请告诉我。

    【讨论】:

      【解决方案3】:

      这是您所要求的单线程版本。

      #include <stdio.h>
      #include <stdlib.h>
      
      /*
      I can not run pthread on my system.
      So this is some code that should kind of work the same way
      */
      typedef int pthread_t;
      typedef int pthread_attr_t;
      typedef void*(*threadfunc)(void*);
      
      int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
      {
          start_routine(arg);
          return 0;
      }
      
      int pthread_join(pthread_t thread, void **value_ptr)
      {
          return 0;
      }
      
      struct context
      {
          int* begin;
          int* end;
          int* result;
      };
      
      //the function has to be castable to the threadfunction type
      //that way you do not have to worry about casting the argument.
      //be careful though - if something does not match these errors may be hard to track
      void * smallest(context * c) //signature needet for start routine
      {
          c->result = c->begin;
          for (int* current = c->begin; current < c->end; ++current)
          {
              if (*current < *c->result)
              {
                  c->result = current;
              }
          }
          return 0; // not needet with the way the argument is set up.
      }
      
      int main(int argc, char *argv[])
      {
          pthread_t t1, t2;
      #define size 20
          int array[size];
          srand(0);
          for (int i = 0; i < size; ++i)
          {
              array[i] = (rand() % 100) + 1;
              printf("%d ", array[i]);
          }
      
          //prepare data
          //one surefire way of messing up in multithreading is sharing data between threads.
          //even a simple approach like storing in a variable who is accessing will not solve the issues
          //to properly lock data you would have to dive into the memory model.
          //either lock with mutexes or memory barriers or just don' t share data between threads.
          context c1;
          context c2;
          c1.begin = array;
          c1.end = array + (size / 2);
          c2.begin = c1.end + 1;
          c2.end = array + size;
      
          //start threads - here your threads would go
          //note the casting - you may wnt to wrap this in its own function
          //there is error potential here, especially due to maintainance etc...
          pthread_create(&t1, 0, (void*(*)(void*))smallest, &c1); //without typedef
          pthread_create(&t2, 0, (threadfunc)smallest, &c2); //without typedef
      
          pthread_join(t1, 0);//instead of zero you could have a return value here
          pthread_join(t1, 0);//as far as i read 0 throws the return value away
          //return value could be useful for error handling
      
          //evaluate
          if (*c1.result < *c2.result)
          {
              printf("\nThread1 finds the number\n");
              printf("The smallest element is %i\n", *c1.result);
          }
          else
          {
              printf("\nThread2 finds the number\n");
              printf("The smallest element is %i\n", *c2.result);
          }
      
          return 0;
      }
      

      编辑: 我编辑了一些存根,让您了解如何使用多线程。 我从未使用过pthread,但这应该可以。 我使用this 来源获取原型信息。

      【讨论】:

      • 我完全理解你的代码是如何工作的,非常感谢你抽出时间来帮助我,但是为了我的爱,我不知道如何对结构和参数进行类型转换我不断收到很多错误和警告。如果您能帮助我,我将不胜感激,但如果不能,我明白了,再次感谢您!
      • @imaginedrragon 不确定我是否捕获了您的确切问题。我编辑希望能更接近您的需要。
      猜你喜欢
      • 2010-12-11
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多