如何在 C++ 中正确地在循环中使用互斥锁?

Posted

技术标签:

【中文标题】如何在 C++ 中正确地在循环中使用互斥锁?【英文标题】:How to use mutex in loops properly in c++? 【发布时间】:2020-08-16 15:57:01 【问题描述】:

我是线程和互斥锁的新手,我正在尝试学习它们。我编写了一些代码,通常创建一个队列,将文件中的所有数字排入队列(该文件有近 20.000 行和大量信息),因为该文件包含大量信息,用于处理我需要多线程,在开始用户创建线程数,然后我停留在while循环中我想查看哪些线程进入循环以从队列中取出id,但显然只有第一个创建的线程进入并将它们全部取出,我使用互斥锁确保当一个线程进入循环时让它处理(出队一个数字)然后解锁这个互斥锁以便其他线程可以进入但显然我做错了。这是代码`

void printer( DynIntQueue  & myQueue) // takes the Dynamic Queue 

    //int count = 0;
    queMutex.lock(); // lock it before any threads come it 
    while(!myQueue.isEmpty()) // check this condition
    
        int num;
        
        cout << "Thread " << this_thread::get_id << " is working" << endl; // this is for printing out which threads enter this loop
        myQueue.dequeue(num); // deqeueu this number from queue
        
        queMutex.unlock(); // unlock this in order to next thread might enter
        queMutex.lock();   // when this thread enters lock it in order to other threads to wait
        
        
    
    queMutex.unlock(); // if myQueue is empty since it is firsly locked, unlock this 
    
    
`

我的输出是这样的: Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working Thread 005C1659 is working 这种情况一直持续到 myQueue 使用相同的线程为空。我能做些什么来确保其他线程可能进入这个循环?

已编辑:这是主要部分`

int main()

    DynIntQueue firstQueue;
    
    ifstream input;
    string line;
    int numofthreads;

    input.open("data.tsv");
    getline(input, line); // for first empty
    int id, houseAge, avgRooms, avgBedRooms, latitue, longitute, medianPrice;

    cout << "Please enter the number of threads you want " << endl;
    cin >> numofthreads;
    
    vector <thread> Threads(numofthreads);

    while (!input.eof())
    
        getline(input, line);
        istringstream divider(line);
        divider >> id >> houseAge >> avgRooms >> avgBedRooms >> latitue >> longitute >> medianPrice;
        firstQueue.enqueue(id);
    

    for (int i = 0; i < numofthreads; i++)
    
        Threads[i] = thread(&printer, ref(firstQueue));

       
    
     for (int i = 0; i < numofthreads; i++)
     
         Threads[i].join();
     
     
     return 0;

【问题讨论】:

问题可能是解锁和锁定之间没有时间间隔。您可以让当前线程休眠一段时间,以便模拟在那里完成的一些工作。 另外,您没有向我们展示线程是如何创建的。请发complete minimal compilable example code。 这是一个有问题的推理线:“因为这个文件包含大量的进程信息,我需要多线程”。这个结论不是从前提得出的。 必读:Why is iostream::eof inside a loop condition (i.e. while (!stream.eof())) considered wrong? 也可能:Why is “using namespace std;” considered bad practice?. 【参考方案1】:

注意:std::this_thread::get_id() 是一个函数,所以你应该调用它。我认为这只是一个复制/粘贴错误。

如果我在打开和关闭队列之间添加一些工作,我会清楚地看到两个线程在使用队列。

我认为您对显示的代码没有任何问题。

#include <iostream>
#include <thread>
#include <mutex>
#include <queue>


struct DynIntQueue 
  bool isEmpty() const  return q_.empty(); 
  void dequeue(int &elem)  elem = q_.front(); q_.pop(); 
  std::queue<int> q_10, 20, 4, 8, 92;
;

std::mutex queMutex;

void printer( DynIntQueue  & myQueue) 
    queMutex.lock();
    while(!myQueue.isEmpty()) 
      int num;
      std::cout << "Thread " << std::this_thread::get_id() << " is working" << std::endl;
      myQueue.dequeue(num);
      queMutex.unlock();
      std::cout << "working outside the lock" << std::endl;
      std::cout << "working outside the lock" << std::endl;
      std::cout << "working outside the lock" << std::endl;
      std::cout << "working outside the lock" << std::endl;
      std::cout << "working outside the lock" << std::endl;
      queMutex.lock();
    
    queMutex.unlock();   


int main() 
  std::cout << "Hello World!\n";
  DynIntQueue q;

  std::thread t1([&q]()  printer(q); );
  std::thread t2([&q]()  printer(q); );
  t1.join();
  t2.join();

$ clang++-7 -pthread -std=c++17 -o main main.c
$ ./main
Hello World!
Thread 139686844172032 is working
working outside the lock
working outside the lock
working outside the lock
working outside the lock
working outside the lock
Thread 139686835779328 is working
working outside the lock
working outside the lock
working outside the lock
working outside the lock
working outside the lock
Thread 139686844172032 is working
working outside the lock
working outside the lock
working outside the lock
working outside the lock
working outside the lock
Thread 139686835779328 is working
working outside the lock
working outside the lock
working outside the lock
working outside the lock
working outside the lock
Thread 139686844172032 is working
working outside the lock
working outside the lock
working outside the lock
working outside the lock
working outside the lock

【讨论】:

我在解锁和锁定之间使用了睡眠 1 秒的方法我这里仍然有问题,我也用主要部分再次编辑。 @Razbolt:您是否将 numOfThreads 指定为 1?您能否重写您的 main 使其不需要用户输入(我建议像我在此答案中所做的那样对其进行硬编码)。 另外,我假设这只是一个复制粘贴错误,但 std::this_thread::get_id() 是一个函数,所以你应该调用它。 @BillLynch 很好,这似乎是问题所在,因为输出数字是 OP 的十六进制数,默认情况下是指针。 OP 可能会打印出get_id 函数的地址。你应该把它放在答案中。 非常感谢,这是一个 get_id() 问题,我为这个问题挣扎了 2 天让我感到恶心:D

以上是关于如何在 C++ 中正确地在循环中使用互斥锁?的主要内容,如果未能解决你的问题,请参考以下文章

如何在c中正确同步多线程与互斥锁?

如何正确销毁 pthread 互斥锁

在 C++ 中使用互斥锁和条件变量实现带有信号的监视器

使用原子和互斥锁 c++ 在类内部进行线程化

在 windows 中使用互斥锁进行进程间同步(win32 或 C++)

锁定未锁定的互斥体的效率如何?互斥锁的成本是多少?