nwp*_*nwp 23 c++ multithreading llvm-clang c++14
给出以下示例代码:
int var;
int mvar;
std::mutex mvar_mutex;
void f(){
mvar_mutex.lock();
mvar = var * var;
mvar_mutex.unlock();
}
Run Code Online (Sandbox Code Playgroud)
我想表达它mvar_mutex绑定到变量mvar并仅保护该变量.mvar_mutex不应该保护,var因为它不受约束.因此,允许编译器将上面的代码转换为以下代码:
int var;
int mvar;
std::mutex mvar_mutex;
void f(){
int r = var * var; //possible data race created if binding is not known
mvar_mutex.lock();
mvar = r;
mvar_mutex.unlock();
}
Run Code Online (Sandbox Code Playgroud)
这可能会减少锁定上的争用,因为在保持锁定时工作量较少.
为此int可以使用std::atomic<int> mvar;和删除mvar_mutex,但对于其他类型,std::vector<int>这是不可能的.
如何以C++编译器理解并进行优化的方式表达互斥变量绑定?对于未绑定到该互斥锁的任何变量,应该允许在互斥锁边界上向上或向下重新排序任何变量
由于代码是使用生成的,clang::ASTConsumer并且clang::RecursiveASTVisitor我愿意使用非标准扩展和AST操作,只要clang(理想情况下是clang 4.0)支持它们并且生成的代码不需要优雅或人类可读.
编辑,因为这似乎导致混淆:上述转换在C++中是不合法的.所描述的互斥锁与变量的绑定不存在.问题是如何实现或实现相同的效果.
Aki*_*ira 10
如果您希望实现该std::mutex操作只会在对受保护对象执行操作之前保留,您可以编写一个包装器class,如下所示:
#include <cstdio>
#include <mutex>
template<typename T>
class LockAssignable {
public:
LockAssignable& operator=(const T& t) {
std::lock_guard<std::mutex> lk(m_mutex);
m_protected = t;
return *this;
}
operator T() const {
std::lock_guard<std::mutex> lk(m_mutex);
return m_protected;
}
/* other stuff */
private:
mutable std::mutex m_mutex;
T m_protected {};
};
inline int factorial(int n) {
return (n > 1 ? n * factorial(n - 1) : 1);
}
int main() {
int var = 5;
LockAssignable<int> mvar;
mvar = factorial(var);
printf("Result: %d\n", static_cast<int>(mvar));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,factorial将提前计算并且m_mutex仅在调用赋值或隐式转换运算符时才会获取mvar.
对于原始数据类型可以使用std::atomic与std::memory_order_relaxed.文件指出:
没有对其他读取或写入施加同步或排序约束,只保证此操作的原子性
在以下示例中,保证了赋值的原子性,但编译器应该能够移动操作.
std::atomic<int> z = {0};
int a = 3;
z.store(a*a, std::memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)
对于对象,我想到了几个解决方案,但是:
std::mutex.std::atomic<std::vector>.std::memory_order_relaxed(参见示例)无法创建自旋锁.我找到了一些答案,说明:
因此,为了表示mvar_mutex绑定到变量,您可以使用其他答案所述的某些类,但我认为不可能完全允许重新排序代码.
锁定的 var 模板怎么样?
template<typename Type, typename Mutex = std::mutex>
class Lockable
{
public:
Lockable(_Type t) : var_(std::move(t));
Lockable(_Type&&) = default;
// ... could need a bit more
T operator = (const T& x)
{
std::lock_guard<Lockable> lock(*this);
var_ = x;
return x;
}
T operator *() const
{
std::lock_guard<Lockable> lock(*this);
return var_;
}
void lock() const { const_cast<Lockable*>(this)->mutex_.lock(); }
void unlock() const { const_cast<Lockable*>(this)->mutex_.unlock().; }
private:
Mutex mutex_;
Type var_;
};
Run Code Online (Sandbox Code Playgroud)
被赋值运算符锁定
Lockable<int>var;
var = mylongComputation();
Run Code Online (Sandbox Code Playgroud)
与 lock_guard 配合使用效果很好
Lockable<int>var;
std::lock_guard<Lockable<int>> lock(var);
var = 3;
Run Code Online (Sandbox Code Playgroud)
在容器上实用
Lockable<std::vector<int>> vec;
Run Code Online (Sandbox Code Playgroud)
ETC...
| 归档时间: |
|
| 查看次数: |
1050 次 |
| 最近记录: |