【问题标题】:Where to lock and unlock the mutexes?在哪里锁定和解锁互斥锁?
【发布时间】:2021-11-23 20:38:10
【问题描述】:

我正在编写一个程序,其中有两个线程,第一个线程扫描输入文件(每行一个 int)并将数字分配给全局变量。然后第二个线程读取全局变量,如果偶数将其打印到文件中两次,如果奇数则仅打印一次。

例如,如果输入文件是:

1
2
3

那么输出文件应该是:

1
2
2
3

不幸的是,输出文件如下:

3

我可以在哪里放置我的互斥锁和解锁以获得正确的结果?

这是我的代码:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <iostream>
#include <cstdlib>

using namespace std;

FILE* ifile;
int variable = 0;
pthread_mutex_t thing;

struct arguments {
  FILE* file;
};

void* meth1(void* param) {
  ifile = fopen("intput.txt", "r");
  if (ifile == NULL) {
    printf("couldn't open the file");
    return 0;
  }

  pthread_mutex_lock(&thing);
  while (!feof(ifile)) {
    fscanf(ifile, "%d\n", &variable);
  }
  pthread_mutex_unlock(&thing);

  return NULL;
}

void* meth2(void* param) {
  if (variable % 2 == 0) {
    pthread_mutex_lock(&thing);
    fprintf((FILE*)param, "%d\n", variable);
    fprintf((FILE*)param, "%d\n", variable);
    pthread_mutex_unlock(&thing);
  }
  if (variable % 2 != 0) {
    pthread_mutex_lock(&thing);
    fprintf((FILE*)param, "%d\n", variable);
    pthread_mutex_unlock(&thing);
  }

  return NULL;
}

int main() {
  FILE* ofile;

  ofile = fopen("output.txt", "w");
  if (ofile == NULL) {
    printf("couldn't open the file");
    return 0;
  }

  arguments args;
  args.file = ofile;

  pthread_t thread1;
  pthread_create(&thread1, NULL, meth1, NULL);
  pthread_join(thread1, NULL);

  pthread_t thread2;
  pthread_create(&thread2, NULL, meth2, args.file);
  pthread_join(thread2, NULL);

  fclose(ofile);
  pthread_mutex_destroy(&thing);
  return 0;
}

【问题讨论】:

  • 在这个例子中,我认为你根本不需要任何互斥锁。线程 1 在线程 2 之前完成(因为您调用了 join),并且没有同时共享任何变量。然而,不要使用 pthreads,而是看看 std::async (& std::future),或者至少是 std::thread、std::mutex 和 std::unique_lock。 (en.cppreference.com/w/cpp/thread/async)
  • 即使这些线程同时运行,代码也不会工作。 meth1 锁定互斥体,然后从输入文件中读取值,直到到达文件末尾。所以它读取的最后一个值是variable。然后它解锁互斥锁。如果幸运的话,meth2 会看到写入的值。如果你不走运,meth2 将已经运行,并且看到值 0。这种线程的锁步操作非常棘手,而且通常毫无意义。
  • 我建议研究如何实施生产者-消费者系统。这是您尝试在此处创建的系统的名称。
  • 生产者/消费者看起来像这样,现场演示onlinegdb.com/mjgUFDgG8

标签: c++ pthreads mutex


【解决方案1】:

在你的 for 循环中你这样做:

pthread_mutex_lock(&thing);
while (!feof(ifile)) {
    fscanf(ifile, "%d\n", &variable);
}
pthread_mutex_unlock(&thing);

因此,您正在使用整个文件并在每个循环中分配给变量。 除了3之外,没有机会写任何东西。

【讨论】:

    【解决方案2】:

    编辑:

    以下代码不直接回答问题,但它是您问题的解决方案。

    #include <stdlib.h>
    #include <stdio.h>
    #include <iostream>
    #include <fstream>
    #include <string.h>
    
    using namespace std;
    
    int main() 
    {
    
        fstream f_input;
        fstream f_output;
    
        /* Open input file */
        f_input.open("input.txt",ios::in);
        if (!f_input.is_open())
        {
            cout << "Unable to open input file" << endl;
            return 0;
        }
    
        /* Open out file */
        f_output.open("output.txt",ios::out);
        if (!f_output.is_open())
        {
            cout << "Unable to open output file" << endl;
            return 0;
        }
    
        /* Iterate thru the file per line*/
        string last_line;
        char *p_last_line;
        int  i_last_line_integer;
        unsigned int ui_last_line_len;
        do
        {   
            getline(f_input, last_line);
            p_last_line = (char *)last_line.data();
            sscanf(p_last_line,"%d", &i_last_line_integer);
            ui_last_line_len = last_line.length();
    
            if (i_last_line_integer %2 == 0) /* it's even, write twice */
            {
                f_output.write(p_last_line,ui_last_line_len);
                f_output.write("\n", ui_last_line_len); 
                f_output.write(p_last_line,ui_last_line_len);
                f_output.write("\n", ui_last_line_len); 
            }
            else    /* it's odd, write once */
            {
                f_output.write(p_last_line,ui_last_line_len);
                f_output.write("\n", ui_last_line_len); 
            }
        }while(!f_input.eof());
        f_input.close();
        f_output.close();
        return 0;
    }
    

    旧:

    我看到你在创建第一个线程后立即调用了 join 函数。 join 函数等待线程结束,因此如果您希望两个线程同时运行,您应该使用将该部分代码重写为:

    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, meth1, NULL);
    pthread_create(&thread2, NULL, meth2, args.file);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    

    但是我认为这不是您真正想要完成的,您是否希望代码首先读取第一个文件并将数字输出到第二个文件中?如果是这样,您可以在读取变量后一次打开两个文件并在每个循环中打印到第二个文件,因为此时您的第二个线程实际上只执行一次。 现在下一个代码是您应该如何使用互斥锁来防止同时从两个线程访问变量,但如果它的工作是读取并复制到另一个文件以防除法的剩余部分为 0,则不修复算法:

    #include <stdlib.h>
    #include <stdio.h>
    #include <pthread.h>
    #include <iostream>
    #include <cstdlib>
    using namespace std;
    FILE *ifile;
    int variable = 0;
    
    pthread_mutex_t thing;
    struct arguments
    {
        FILE *file;
    };
    
    void *meth1(void *param)
    {
        
        ifile = fopen("intput.txt", "r");
        if (ifile == NULL)
        {
            printf("couldn't open the file");
            return 0;
        }
        
        while (!feof(ifile))
        {
            pthread_mutex_lock(&thing);
            fscanf(ifile, "%d\n", &variable);
            pthread_mutex_unlock(&thing);
        }
        
    
        return NULL;
    }
    
    void *meth2(void *param)
    {
    
        pthread_mutex_lock(&thing);
        if (variable % 2 == 0)
        {
            fprintf((FILE *)param, "%d\n", variable);
            fprintf((FILE *)param, "%d\n", variable);
        }
        else
        {
            fprintf((FILE *)param, "%d\n", variable);
        }
        pthread_mutex_unlock(&thing);
    
        return NULL;
    }
    
    int main()
    {
    
        FILE *ofile;
    
        ofile = fopen("output.txt", "w");
        if (ofile == NULL)
        {
            printf("couldn't open the file");
            return 0;
        }
    
        arguments args;
        args.file = ofile;
    
        pthread_t thread1, thread2;
    
        pthread_create(&thread1, NULL, meth1, NULL);
        pthread_create(&thread2, NULL, meth2, args.file);
        pthread_join(thread1, NULL);
        pthread_join(thread2, NULL);
    
        fclose(ofile);
        pthread_mutex_destroy(&thing);
        return 0;
    }
    

    【讨论】:

    • 我会将fscanf 放在锁/临界区之外,并将解析后的值存储在一个临时变量中。在锁/临界区,我只会更新共享数据:variable = tmp;fscanf 可能有点贵,不需要过度占用锁。哦,顺便说一句,while (!feof(ifile)) {...} 是“总是”错误的。但你可能知道。
    • 很遗憾,修改后的代码不保证能正常工作。在meth2 尝试获取互斥体之前,没有什么可以阻止meth1 遍历所有输入数据。而且,反过来,没有什么可以阻止meth2meth1 为其分配值之前读取variable,因此meth2 将看到值0。最后,meth2 只应用于一个variable 的值,然后返回。正如我在对答案的评论中所说,两个线程之间的这种同步协调很难做到正确。它需要一个同步队列。
    猜你喜欢
    • 1970-01-01
    • 2018-05-23
    • 2021-12-23
    • 2022-07-31
    • 2012-12-25
    • 2021-02-03
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多