Semaphore/Mutex 和 Printf 之间的同步

Posted

技术标签:

【中文标题】Semaphore/Mutex 和 Printf 之间的同步【英文标题】:Synchronization between Semaphore/Mutex and Printf 【发布时间】:2017-05-14 22:03:20 【问题描述】:

我正在为我的操作系统课程做一个关于信号量和同步的练习(参见下面的粗体文本)。练习的文字是这样的:

Pthread 信号量和互斥锁

C 程序 gen_binary_numbers.c 在命令行接收一个整数 n,并使用递归生成并显示所有 n 位二进制数。将递归程序转换为并发程序,将递归过程替换为生成适当数量的显示二进制数的进程(以任何顺序)。

这是我的代码,实际上:

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

int num, r, c;
pthread_mutex_t mutex;
void *genBin(void *arg);

int main (int argc, char **argv) 
  if (argc != 2) 
    fprintf(stdout, "\nUSAGE: %s <n>\n\n", argv[0]);
    exit(EXIT_FAILURE);
  

  int i;
  num = atoi(argv[1]);
  c = num;
  r = 2;
  for (i=1; i<num; i++) 
    r=r*2;
  

  pthread_mutex_init(&mutex, NULL);
  pthread_t* p;
  p = malloc(r*sizeof(pthread_t));

  for (i=0;i<r;i++) 
    if (pthread_create(&p[i], NULL, genBin, &i)) 
      fprintf(stderr, "Error creating thread.\n");
      exit(EXIT_FAILURE);
    
  
  pthread_exit(0);


void *genBin (void *arg) 
  int x;
  int i=0;
  x = *((int*)arg);

  pthread_mutex_lock(&mutex);

  while (i<num) 
    if(x!=0) 
      fprintf(stdout, "%d", x%2);
    
    else 
      fprintf(stdout, "0");
    
    i++;
    x/=2;
  
  fprintf(stdout, "\n");
  pthread_mutex_unlock(&mutex);
  pthread_exit(0);

我认为代码应该返回正确的解决方案,但有时输出没有返回正确的数字。

正确输出示例:

./genBin 3
100
101
010
110
001
011
111
000

错误输出示例(由于重复):

./genBin 3
110
110
110
001
011
111
111
000

我认为问题在于互斥锁和 printf 之间的同步。 是否有替代解决方案来避免混淆结果?

【问题讨论】:

问题陈述说你要创建多个进程,但是你的程序却创建了线程。这些不是一回事,虽然两者有相似的考虑,但细节有所不同。 另外,你对失败行为的性质有点模糊。错误输出的示例和错误原因的解释将是适当的。 你说得对,但我忘了告诉你,实验室的标题是Pthread semaphores and mutexes。所以,我认为我必须使用 pthreads。我会在文中更正。谢谢! 【参考方案1】:

感谢大家!你解决了我的问题。这是更正后的代码:

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

int num, r, c;
pthread_mutex_t mutex;
void *genBin(void *arg);

int main (int argc, char **argv) 
  if (argc != 2) 
    fprintf(stdout, "\nUSAGE: %s <n>\n\n", argv[0]);
    exit(EXIT_FAILURE);
  

  int i;
  int *temp;
  num = atoi(argv[1]);
  c = num;
  r = 2;
  for (i=1; i<num; i++) 
    r=r*2;
  

  temp = malloc(r*sizeof(int));
  pthread_mutex_init(&mutex, NULL);
  pthread_t* p;
  p = malloc(r*sizeof(pthread_t));

  for (i=0;i<r;i++) 
    temp[i] = i;  
  

  for (i=0;i<r;i++) 
    if (pthread_create(&p[i], NULL, genBin, &temp[i])) 
      fprintf(stderr, "Error creating thread.\n");
      exit(EXIT_FAILURE);

    
  

  for (i=0;i<r;i++) 
    if (pthread_join(p[i], NULL)) 
      fprintf(stderr, "Error creating thread.\n");
      exit(EXIT_FAILURE);
    
  

  pthread_mutex_destroy(&mutex);
  free(temp);
  free(p);
  pthread_exit(0);


void *genBin (void *arg) 
  int x;
  int i=0;
  int *v;
  v = malloc(num*sizeof(int));
  x = *((int*)arg);

  for (i=0; i<num; i++) 
    v[i] = x%2;
    x/=2;
  

  pthread_mutex_lock(&mutex);
  for (i=0; i<num; i++) 
    fprintf(stdout, "%d", v[i]);
  
  fprintf(stdout, "\n");
  pthread_mutex_unlock(&mutex);

  free(v);
  pthread_exit(0);

【讨论】:

请注意,存在内存泄漏(你不是 free()'ing 任何东西)。这在大多数现代操作系统上都不是问题,因为内存会在退出时回收。只是需要注意的事情。此外,由于您的代码中不涉及共享资源,因此不需要锁定。它的目的似乎是避免交错输出。但是您可以通过将结果传递回 main 并在最后打印它们来修复。 我总是忘记做free(),我会尽快在代码中更正它,谢谢。我必须在本练习中使用互斥锁,但感谢您的提示! 您刚刚添加的 pthread_mutex_destroy() 和 free() 调用有问题。如果主线程其他线程之前退出,则temp[x] 可能无效。同样,当 main 销毁互斥锁​​时,其他线程可能仍在使用互斥锁。一种方法是将主线程作为最后一个退出,即您可以在循环中执行 pthread_join,然后释放资源,而不是在 main 中调用 pthread_exit。或者,您也可以删除这些释放,因为这些泄漏在您的代码的几乎所有使用中都不会成为问题。 如果稍微改变,则根本不需要互斥锁。【参考方案2】:

您的代码包含竞争条件。在 main 中,您将迭代变量的地址 i 作为线程函数的参数传递。然后每个新线程与主线程竞争以在主线程递增之前读取i 的值(通过提供的指针)。解决该问题的一种方法是使用信号量让主线程在创建每个线程后等待,直到该线程取消引用其参数。

另外,我认为您不需要在genBin() 中使用互斥锁。它访问的唯一共享数据是stdout,通过fprintf(),并且该函数的操作就像它锁定了与指定流相关联的独占锁。此外,使用互斥锁,您基本上不会获得实际的并发性,因为每个线程几乎在其执行的整个持续时间内都将互斥锁保持锁定。

【讨论】:

谢谢,我用竞争条件更改了部分,并移动了互斥锁的锁定/解锁,以便仅包围 printf 的部分。【参考方案3】:

问题出在这部分:

  for (i=0;i<r;i++) 
    if (pthread_create(&p[i], NULL, genBin, &i)) 
      fprintf(stderr, "Error creating thread.\n");
      exit(EXIT_FAILURE);
    
  

data race,因为您将i 的地址传递给所有线程。您可以使用临时数组将单个数字传递给每个线程。

【讨论】:

以上是关于Semaphore/Mutex 和 Printf 之间的同步的主要内容,如果未能解决你的问题,请参考以下文章

Windows线程同步

RT-Thread RTOS的RT-Thread / uCOS / FreeRTOS 简单比较

如何监视和记录哪些模块在 linux 内核中持有锁?

RTX线程通信之——消息队列

RTX线程通信之——消息队列

操作系统考研习题讲解——生产者消费者习题001