在处理读写时,我应该锁定哪个部分?

时间:2015-04-01 03:27:02

标签: c++ multithreading locking mutex

我正在使用C ++(11)STL并遇到以下问题。 这段代码的基本思想是: 我有一个“触发器”功能,一个“添加”功能和一个标志(默认为假)。如果标志为false,则“add”函数将threadID推送到队列,否则它将把threadID插入到集合中。当调用触发器函数时,它将标志设置为“true”并将threadID从队列移动到集合。 我初始化了100个线程并使用其中一个线程来运行触发器函数(在代码中它的线程编号为30)。理想情况下,结果应该在队列中有0个元素,在集合中有99个元素。 但是,有时结果是正确的,有时我错过了集合中的一些数字,有时我得到了EXC_BAD_ACCESS错误。 有人可以帮忙吗?谢谢。

#include <iostream>
#include <thread>
#include <vector>
#include <unordered_set>
#include <queue>
#include <mutex>
#include <atomic>
using namespace std;


bool flag = false;
queue<int> q;
unordered_set<int> s;
mutex mu;

void trigger()
{
    mu.lock();
    flag = true;
    mu.unlock();
    while( !q.empty() ){
        s.insert(q.front());
        q.pop();
    }
}

void add(int id)
{
    mu.lock();
    if( !flag ) 
        q.push(id);
    else {
        if ( s.find(id) == s.end() ){
            s.insert(id);
        }
    }
    mu.unlock();
}

void missing()
{
    cout << "Missing Numbers: ";
    for (int i = 1; i <= 100; i++) {
        if( s.find(i) == s.end() ) 
            cout << i << " ";
    }
    cout << endl;
}

int main()
{
    vector<thread> threads;

    for (int i = 0; i < 100; i++){
        if ( i == 29 ) threads.push_back(thread(trigger));
        else threads.push_back(thread(add, i+1));
    }
    for (int i = 0; i < 100; i++){
        threads[i].join();
    }
    cout << "Q size: " << q.size() << endl;
    cout << "S size: " << s.size() << endl;
    missing();
}

1 个答案:

答案 0 :(得分:0)

您有一个执行trigger函数的线程和许多执行add函数的线程。此外,你要注意保护一些共享状态,但不是全部。请参阅以下代码段中的我的评论/问题。

void trigger()
{
    // Only the 'flag' is protected from concurrent acceess
    mu.lock();
    flag = true;
    mu.unlock();

    // Why isn't 'q' or 's' protected by a lock?
    while( !q.empty() ){
        s.insert(q.front());
        q.pop();
    }
}

void add(int id)
{
    // In this function both 'q' and 's' are protected from concurrent access
    mu.lock();
    if( !flag ) 
        q.push(id);
    else {
        if ( s.find(id) == s.end() ){
            s.insert(id);
        }
    }
    mu.unlock();
}

可能的解决方案

通常,您应该保护同时访问的任何状态。我还建议使用 lock 类型(例如lock_guard),而不是直接锁定和解锁互斥锁(研究RAII以了解为何会鼓励这种情况)。

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

using namespace std;

bool flag = false;
queue<int> q;
unordered_set<int> s;
mutex mu;

void trigger()
{
    lock_guard<mutex> lock(mu);
    flag = true;

    while (!q.empty())
    {
        s.insert(q.front());
        q.pop();
    }
}

void add(int id)
{
    lock_guard<mutex> lock(mu);

    if (!flag)
    {
        q.push(id);
    }
    else
    {
        if (s.find(id) == s.end())
        {
            s.insert(id);
        }
    }
}

void missing()
{
    cout << "Missing Numbers: ";
    for (int i = 1; i <= 100; ++i)
    {
        if (s.find(i) == s.end())
        {
            cout << i << " ";
        }
    }
    cout << endl;
}

int main()
{
    vector<thread> threads;
    for (int i = 0; i < 100; ++i)
    {
        if (i == 29)
        {
            threads.push_back(thread(trigger));
        }
        else
        {
            threads.push_back(thread(add, i + 1));
        }
    }

    for (int i = 0; i < 100; ++i)
    {
        threads[i].join();
    }

    cout << "Q size: " << q.size() << endl;
    cout << "S size: " << s.size() << endl;
    missing();

    return 0;
}