我知道,当我在多个线程内的单个STL容器上执行操作时,我需要使用互斥锁.但是,我想知道此规则是否有任何例外.请考虑我正在尝试实施的简化方案.
我有多个线程向容器添加元素,操作包含互斥锁定/解锁.然后线程以某种方式通知(例如在linux上使用eventfd)单线程专用于调度此容器中的元素.我想要做的是访问容器中的第一个元素而不使用互斥锁.示例代码基于deque,但请注意我可以使用任何具有队列功能的容器:
std::mutex locker;
std:deque<int> int_queue;
int fd; // eventfd
eventfd_t buffer;
bool some_condition;
Run Code Online (Sandbox Code Playgroud)
线程1,2,3等
locker.lock ();
int_queue.push_back (1);
locker.unlock ();
eventfd_write (fd, 1);
Run Code Online (Sandbox Code Playgroud)
专用于调度元素的线程:
while (true)
{
bool some_condition (true);
locker.lock ();
if (int_quque.empty () == false)
{
locker.unlock ();
}
else
{
locker.unlock ();
eventfd_read (fd, &buffer);
}
while (some_condition)
{
int& data (int_queue.front ());
some_condition = some_operation (data); // [1]
}
locker.lock ();
int_queue.pop ();
locker.unlock ();
}
Run Code Online (Sandbox Code Playgroud)
[1]我会多次对signle元素执行some_operation(),这就是为什么我想在这里避免互斥锁定.这很贵.
我想知道这段代码是否会导致任何同步问题.
你需要的是参考稳定性.也就是说,当容器是push_back'd时,如果第一个元素的引用没有失效,那么你可以这样使用容器.即便如此,您还是希望获得锁定前面元素的引用.
我std::condition_variable对事件通知比较熟悉,所以我会用它:
#include <mutex>
#include <condition_variable>
#include <deque>
std::mutex locker;
std::deque<int> int_queue;
std::condition_variable cv;
void thread_1_2_3()
{
// use lock_guard instead of explicit lock/unlock
// for exception safety
std::lock_guard<std::mutex> lk(locker);
int_queue_.push_back(1);
cv.notify_one();
}
void dispatch()
{
while (true)
{
bool some_condition = true;
std::unique_lock<std::mutex> lk(locker);
while (int_queue.empty())
cv.wait(lk);
// get reference to front under lock
int& data = int_queue.front();
lk.unlock();
// now use the reference without worry
while (some_condition)
some_condition = some_operation(data);
lk.lock();
int_queue.pop_front();
}
}
Run Code Online (Sandbox Code Playgroud)
23.3.3.4 [deque.modifiers]说这个push_back:
在deque两端的插入使deque的所有迭代器无效,但对deque元素的引用的有效性没有影响.
这是允许您在锁外部挂起该引用的关键.如果thread_1_2_3 在中间开始插入或删除,则您不能再挂起此引用.
你不能这样使用vector.但你可以用list这种方式.检查要使用此方式的每个容器以获得参考稳定性.