【问题标题】:Why is mutex lock not working for thread-safe queue class implemented by linked list?为什么互斥锁对链表实现的线程安全队列类不起作用?
【发布时间】:2021-06-01 06:19:14
【问题描述】:

我用链表实现了一个队列,我希望多个线程以正确的顺序推送元素。我使用互斥锁锁定了我的成员函数,但终端打印的数字仍然不正确,应该是 1、2、3、4、5、6。而是打印错误的顺序,例如 3,1,4,2,5,6。这意味着互斥锁 DIDNT 起作用。有人可以告诉我为什么吗?以及如何解决这个问题?

// Implement a queue<int>,need functions: push_front, back, pop_back, and print
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

struct ListNode { 
    ListNode* next;
    int val;
    ListNode(int x) {
        val = x;
        this->next = nullptr;
    }
};

class Queue {
    
public:
    Queue() {
        head = new ListNode(0); // dummy head
        tail = head;
    }

    
    void push_front(int x) { // add node at then end of linked list
        lock_guard<mutex> lock(m);
        tail->next = new ListNode(x);
        tail = tail->next;
        
    }

    int pop_back() { // remove the first node of the linked list
        lock_guard<mutex> lock(m);
        ListNode* back = head->next;
        if (back == nullptr) {
            throw invalid_argument("empty queue");
        }
        head->next = back->next;
        back->next = nullptr;
        return back->val;
    }

    void print() {
        lock_guard<mutex> lock(m);
        ListNode* p = head->next;
        while (p != nullptr) {
            cout << p->val << endl;
            p = p->next;
        }
    }

private:
    mutable mutex m;
    // Implement with linked list
    ListNode* head;
    ListNode* tail;
};


int main() {
    Queue* queue = new Queue();

    // Multiple threads push at the same time --> order is wrong
    thread t1 = thread(&Queue::push_front, queue, 1);
    thread t2 = thread(&Queue::push_front, queue, 2);
    thread t3 = thread(&Queue::push_front, queue, 3);
    thread t4 = thread(&Queue::push_front, queue, 4);
    thread t5 = thread(&Queue::push_front, queue, 5);
    thread t6 = thread(&Queue::push_front, queue, 6);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    t6.join();

    queue->print();
}

【问题讨论】:

  • Mutex 不保证订单。它只会确保不会同时添加到队列中(与共享变量同时发生并可能损坏共享变量)。
  • t1t6 的哪一个线程被安排在第一个取决于机会。没有必要检查其他任何东西。
  • 无关:不需要动态分配queue。考虑将Queue* queue = new Queue(); 替换为Queue queue;,将thread(&amp;Queue::push_front, queue, 1);(和朋友)替换为thread(&amp;Queue::push_front, &amp;queue, 1); 以提供队列的地址,并将queue-&gt;print(); 替换为queue.print();。一般来说,Why should C++ programmers minimize use of 'new'?
  • 请注意,如果您希望 push_front() 操作以严格的顺序执行,则无需生成任何线程;相反,您可以直接从主线程调用 push_front() 。能够异步地做事(因此没有任何固定的顺序)是使用线程的主要原因。

标签: c++ multithreading class c++11 mutex


【解决方案1】:

正如许多用户所提到的,您对互斥锁含义的假设至少在(线程)顺序方面是错误的。一般来说,多线程上下文中的顺序是一种品质,它几乎总是系统地来自您的业务逻辑和合适的数据类型,而不仅仅是来自单一的编译器/语言内在函数。

您可以使用简单的单线程连续方法来解决您的问题,或者根据您的进一步目的,您可以将基础数据类型更改为支持部分排序​​的类型(例如在 std::set 的意义上) 结合并行写访问(不单独引用一个容器互斥体,减少整个并行方法的荒谬性)。有些事情在这里是可能的,但这真的取决于你的实际具体要求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 2012-07-06
    • 2013-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多