读和写都要锁住哪一部分?
Which part should I lock when dealing with both read and write?
我正在使用 C++(11) STL 并遇到以下问题。
这段代码的基本思想是:
我有一个 "trigger" 函数,一个 "add" 函数和一个标志(默认为 false)。如果标志为 false,"add" 函数会将 threadID 推入队列,否则会将 threadID 插入集合。当触发函数被调用时,它将标志设置为 "true" 并将 threadIDs 从队列移动到集合中。
我初始化了 100 个线程并将其中一个线程用于 运行 触发函数(在代码中它是线程 NO.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 个线程执行 trigger
函数,许多线程执行 add
函数。此外,您需要注意保护一些共享状态,而不是全部。在下面的代码片段中查看我的 comments/questions。
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;
}
我正在使用 C++(11) STL 并遇到以下问题。 这段代码的基本思想是: 我有一个 "trigger" 函数,一个 "add" 函数和一个标志(默认为 false)。如果标志为 false,"add" 函数会将 threadID 推入队列,否则会将 threadID 插入集合。当触发函数被调用时,它将标志设置为 "true" 并将 threadIDs 从队列移动到集合中。 我初始化了 100 个线程并将其中一个线程用于 运行 触发函数(在代码中它是线程 NO.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 个线程执行 trigger
函数,许多线程执行 add
函数。此外,您需要注意保护一些共享状态,而不是全部。在下面的代码片段中查看我的 comments/questions。
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;
}