对象锁定以实现高效访问
通常,你希望在对其执行多个操作时锁定整个对象。例如,如果需要使用迭代器检查或修改对象。每当需要调用多个成员函数时,锁定整个对象而不是单个成员函数通常更有效。
例如:
class text_buffer
{
// for readability/maintainability
using mutex_type = std::shared_timed_mutex;
using reading_lock = std::shared_lock<mutex_type>;
using updates_lock = std::unique_lock<mutex_type>;
public:
// This returns a scoped lock that can be shared by multiple
// readers at the same time while excluding any writers
[[nodiscard]]
reading_lock lock_for_reading() const { return reading_lock(mtx); }
// This returns a scoped lock that is exclusing to one
// writer preventing any readers
[[nodiscard]]
updates_lock lock_for_updates() { return updates_lock(mtx); }
char* data() { return buf; }
char const* data() const { return buf; }
char* begin() { return buf; }
char const* begin() const { return buf; }
char* end() { return buf + sizeof(buf); }
char const* end() const { return buf + sizeof(buf); }
std::size_t size() const { return sizeof(buf); }
private:
char buf[1024];
mutable mutex_type mtx; // mutable allows const objects to be locked
};
在计算校验和时,对象被锁定以进行读取,允许其他想要同时从对象读取的线程这样做。
std::size_t checksum(text_buffer const& buf)
{
std::size_t sum = 0xA44944A4;
// lock the object for reading
auto lock = buf.lock_for_reading();
for(auto c: buf)
sum = (sum << 8) | (((unsigned char) ((sum & 0xFF000000) >> 24)) ^ c);
return sum;
}
清除对象会更新其内部数据,因此必须使用排除锁来完成。
void clear(text_buffer& buf)
{
auto lock = buf.lock_for_updates(); // exclusive lock
std::fill(std::begin(buf), std::end(buf), '\0');
}
当获得多个锁时,应注意始终为所有线程以相同的顺序获取锁。
void transfer(text_buffer const& input, text_buffer& output)
{
auto lock1 = input.lock_for_reading();
auto lock2 = output.lock_for_updates();
std::copy(std::begin(input), std::end(input), std::begin(output));
}
注意: 最好使用 std::deferred::lock 并调用 std::lock