【问题标题】:Incrementing the global variable using threads without a mutex strangely returns correct value [closed]使用没有互斥锁的线程增加全局变量奇怪地返回正确的值[关闭]
【发布时间】:2018-03-14 13:23:58
【问题描述】:

我们被要求创建 4 个线程,每个线程增加(SPIN/4 倍)全局变量 compteur 的值,以理应注意到每个线程在另一个线程完成迭代之前正在访问/更改全局变量(即为什么SPIN 被赋予了一个非常大的数字),例如线程号 1 首先访问 compteur,而它正在增加另一个线程访问 compteur 并看到 compteur = 0 仍然,最后得出结论,我们必须使用 MUTEX。

问题是程序总是在不应该的时候给我与 SPIN 相同的值。

你能解释一下为什么吗?

#define SPIN 40000000

int compteur = 0;

void *routine_thread(void *arg) {
  int i;
  printf("accessing thread ... \n");

  for (i = 0; i < SPIN / 4; ++i) {
    compteur++;
  }
  printf("quitting thread ... \n");
  pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
  pthread_t thread_id[4];
  void *resultat_thread;
  int statut;
  int i;

  for (i = 0; i < 4; i++) {
    statut = pthread_create(&thread_id[i], NULL, routine_thread, NULL);
    if (statut != 0) {
      fprintf(stderr, "error creating thread\n");
      exit(EXIT_FAILURE);
    }

    statut = pthread_join(thread_id[i], &resultat_thread);
    if (statut != 0) {
      fprintf(stderr, "error joining the thread\n");
      exit(EXIT_FAILURE);
    }
  }

  printf("compteur value is : %d\n", compteur);

  if (resultat_thread == NULL)
    return EXIT_FAILURE;
  else
    return EXIT_SUCCESS;
}

【问题讨论】:

  • 你一次只运行一个线程...
  • 好的,知道了,谢谢
  • 附带说明:您不能通过测试来证明程序没有竞争条件。一些竞争条件很少会导致问题,以至于测试很可能会错过它们。而且,即使您愿意接受由竞争条件引起的少量崩溃/错误;如果您在自己的计算机上进行测试,您无法证明在客户计算机上的费率是可以接受的低水平。

标签: c multithreading pthreads global mutex


【解决方案1】:

您已经序列化了您正在创建的线程。 pthread_join() 循环内的调用在创建下一个线程之前等待线程完成。 因此,您在任何时候都只有一个活动线程(除了主线程)来修改compteur。所以没有你期望观察到的data race

从循环中删除 pthread_join 调用。并创建另一个循环来等待线程:

      for(i= 0; i< 4; i++) {
        statut = pthread_join(thread_id[i], NULL);

        if(statut != 0) {
            fprintf(stderr, "error joining the thread\n");
            exit(EXIT_FAILURE);
        }
      }

您的线程函数不返回任何值。所以你可以在pthread_join调用中使用NULL而不是&amp;resultat_thread

【讨论】:

  • 我最后仍然有相同的值,如果线程同时运行,thread2(例如)如何从 SPIN/4 开始并以 SPIN/4 递增(假设 thread2 正在运行在 thread1 之后,它们是第一个运行的 2 个)
  • 你的意思是如果你按照建议改变,你仍然是 40000000 作为输出?
  • 是的,即使按照建议进行更改,我仍然得到 40000000 作为输出
  • 数据竞争(即多个线程试图以不同步的方式修改compteur)意味着它是未定义的。您可能仍会观察到结果与“预期”一样。但这只是巧合,你不能(也不应该)依赖它。也许,您的系统只有一个处理器(或者忙于其他任务,或者您的线程在下一个线程开始之前完成得太快,等等)并且操作系统随时调度您的任何一个线程。因此,如果看起来,线程会一个接一个地运行。同样,这只是偶然的,您不应该依赖它。
  • 无论您是否可以观察问题,您的程序肯定有问题(=数据竞争)。
【解决方案2】:

在循环中调用pthread_join时,主线程处于空闲状态,等待另一个线程退出。因此,您将获得如下工作流程:

Run thread 0
Wait for thread 0
Thread 0 exited
Run thread 1
Wait for thread 1
Thread 1 exited
...

没有线程同时运行。

你应该分开运行线程循环和等待线程循环。

for(i= 0; i< 4; i++) {
  statut = pthread_create(&thread_id[i], NULL, routine_thread, NULL);           
  if(statut != 0) {
    fprintf(stderr, "error creating thread\n");
    exit(EXIT_FAILURE);                 
  }
}
for(i= 0; i< 4; i++) {
  statut = pthread_join(thread_id[i], &resultat_thread);
  if(statut != 0) {
    fprintf(stderr, "error joining the thread\n");
    exit(EXIT_FAILURE);
  }
}

【讨论】:

  • 我最后仍然有相同的值,如果线程同时运行,thread2(例如)如何从 SPIN/4 开始并以 SPIN/4 递增(假设 thread2 正在运行在 thread1 之后,它们是第一个运行的 2 个)
  • 也许您刚刚启动的线程在开始下一个线程之前已经退出。运行新线程已经够繁重的操作了。
【解决方案3】:
   for(i= 0; i< 4; i++) {

            statut = pthread_create(&thread_id[i], NULL, routine_thread, NULL);         

                     if(statut != 0){
                        fprintf(stderr, "error creating thread\n");
                        exit(EXIT_FAILURE);                 
                     }

            statut = pthread_join(thread_id[i], &resultat_thread);

                    if(statut != 0){
                       fprintf(stderr, "error joining the thread\n");
                       exit(EXIT_FAILURE);
                    }
  }

此代码创建一个线程并等待线程完成它执行。因此全局变量compteur 将被一个线程访问一次,这就是为什么您每次都获得相同的值。

要实现所有线程同时递增(SPIN/4 倍)全局变量compteur 的值的目标,您应该首先创建所有4 个线程pthread_create(),然后调用pthread_join() 等待它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    相关资源
    最近更新 更多