文件说
\n\n\n返回对基础数据的可变引用。\n由于此调用可变地借用互斥体,因此不需要进行实际锁定\xe2\x80\x93 可变借用静态地保证不存在锁。
\n
所以问题是不需要锁定。当然,如果互斥体(in)没有直接从任何不安全块中使用,那么任何线程都不可能持有对其的引用,因此无法使用它。这也意味着,为了编译 get_mut 的调用,如果任何线程正在使用它,则该线程必须已经完成其执行。任何新线程都可以访问受互斥锁保护的数据,因为它将像获取语义一样从内存中读取,因此通过 get_mut 对数据所做的任何更改都将对任何新线程可见。
\n但是,如果从不安全的块中使用互斥体,则某些线程(可能像我的例子中的 ffi 一样)可能仍然在互斥体上有一个指针,并且可以保持锁定并使用数据,或者只是完成数据的变异并解锁互斥体。在前一种情况下,显然是UB,在后一种情况下,这是一个竞争条件,这也是UB,因为调用get_mut的线程可能看不到受保护数据的最新状态,该状态刚刚由另一个线程,它通过指针访问互斥体。为了使最新的更改在调用 get_mut 的线程上可见,应该保留获取/释放语义,但 get_mut 不会调用任何获取操作。
\nget_mut 是一个有用的函数,但我不相信它是线程安全的,因此应该是不安全的。
\nstruct X
{
std::mutex m;
std::string str;
void set(std::string s)
{
auto _ = std::unique_lock(m);
str = std::move(s);
}
~X()
{
// auto _ = std::unique_lock(m);
}
}
Run Code Online (Sandbox Code Playgroud)
标准中是否有任何部分可以保证在没有注释行的情况下~X永远不会在内部遇到竞争条件?~string
对对象和/或生命周期的独占访问可以由具有 RELAXED 语义的原子变量来管理(==除了生命周期结束的事实是同步的之外,没有其他数据)。我们有一个互斥体来保护对对象的访问,因此使用宽松的操作似乎可以同步独占访问/生命周期和互斥体来访问数据。
如果标准要求~mutex与最新同步unlock,并且如果我们将互斥体的声明移到受保护数据下方,那么我们可以承受默认值,~X但如果没有此要求,我们始终需要有显式析构函数来锁定用于保护任何成员的所有互斥体。
PS为了帮助理解我的问题,请使用这个例子https://en.cppreference.com/w/cpp/thread/mutex,有一条评论说g_pages没有锁的访问是安全的。为什么,标准的哪一部分保证了这一点?两个线程连接的事实仅保证不会对映射进行多线程访问,但不能保证与最新mutex::unlock操作的同步关系。我运行许多不同的程序试图暴露竞争条件,并且我非常确定两者的mutex使用以及与之同步的程序必须至少使用一些原子才能工作。但问题是标准不需要使用,而我所知的所有实现都恰好使用它。std::atomic_thread_fencelockunlockthread.joinmutexstd::atomic_thread_fence
#include <chrono>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include <thread>
std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;
void save_page(const std::string &url)
{
// …Run Code Online (Sandbox Code Playgroud)