我有以下代码:
#include <atomic>
int main () {
std::atomic<uint32_t> value(0);
value.fetch_add(1, std::memory_order::relaxed);
static_assert(std::atomic<uint32_t>::is_always_lock_free);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它可以编译,所以这意味着std::atomic<uint32_t>::is_always_lock_free是真的。
然后,使用 gcc 10 和 的汇编代码如下所示-std=c++20 -O3 -mtune=skylake-avx512 -march=skylake-avx512:
0000000000401050 <main>:
401050: c7 44 24 fc 00 00 00 mov DWORD PTR [rsp-0x4],0x0
401057: 00
401058: f0 ff 44 24 fc lock inc DWORD PTR [rsp-0x4]
40105d: 31 c0 xor eax,eax
40105f: c3 ret
Run Code Online (Sandbox Code Playgroud)
许多帖子指出,读-修改-写操作(fetch_add()此处)不能是没有锁的原子操作。
我的问题是std::atomic::is_always_lock_free存在的true真正含义是什么。
该页面说明Equals true if this atomic type is …
我遇到了需要内存中两个值的原子总和的情况.我继承的代码是这样的:
int a = *MemoryLocationOne;
memory_fence();
int b = *MemoryLocationTwo;
return (a + b) == 0;
Run Code Online (Sandbox Code Playgroud)
a和b的单独读取都是原子的,并且代码中其他位置写入这两个内存位置的所有内容也是无锁原子的.然而,问题在于两个位置的值可以并且确实在两个读取之间改变.
那么如何使这个操作成为原子?我知道关于CAS的所有内容,但它往往只涉及使读取 - 修改 - 写入操作原子化,这不是我想要做的.
有没有办法做到这一点,或者是重构代码的最佳选择,这样我只需要检查一个值?
编辑:谢谢,我没有提到我想在第一次修订中无锁地执行此操作,但有些人在我的第二次修订后接受了它.我知道当他们说这样的话时没有人相信人,但我实际上不能使用锁.我必须使用原子来模拟一个互斥体,这比重构代码以跟踪一个值而不是两个值更有用.
目前我的调查方法涉及利用这些值是连续的这一事实,并使用64位读取原子地抓取它们,我确信这些目标平台上是原子的.如果有人有新想法,请捐款!谢谢.
我被告知Clojure具有无锁并发性,这很重要.
我使用了许多语言,但没有意识到他们在幕后执行锁定.
为什么这是Clojure(或任何具有此功能的语言)的优势?
在为即将到来的并发系统考试做准备的过程中,我试图从教科书"多处理器编程的艺术"中完成一些问题.一个问题是困扰我:
练习129:在我们的LockFreeStack对象中使用相同的共享BackOff对象进行推送和弹出是否有意义?我们怎样才能在EliminationBackOffStack中构建空间和时间的退避?
这个问题让我感到困惑,因为我想到的第一件事就是它没有意义,因为所有退避对象都会让进程等待,所以为什么不分享呢?问题的第二部分完全没有我,任何帮助都是最受欢迎的.
LockFreeStack的代码:
public class LockFreeStack<T> {
AtomicReference<Node> top = new AtomicReference<Node>(null);
static final int MIN_DELAY = ...;
static final int MAX_DELAY = ...;
Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
protected boolean tryPush(Node node) {
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node));
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else {
backoff.backoff();
}
}
}
Run Code Online (Sandbox Code Playgroud) 我的问题与多线程无锁同步有关.我想知道以下内容:
实现这一目标的一般方法是什么?我在某处读过有关LockFreePrimitives的内容,比如CompareAndExchange(CAS)或DoubleCompareAndExchange(DCA),但没有给出解释?MINIMIZE使用锁的任何方法?
Java/.NET如何实现其并发容器?他们使用锁或无锁同步吗?
提前致谢.
我想用两种方法创建一个类:
void SetValue(T value) 存储一个值,但只允许存储单个值(否则会抛出异常).T GetValue() 检索值(如果还没有值,则抛出异常).我有以下愿望/约束:
GetValue()只有在最新值不存在时才抛出异常(null):null在调用SetValue()另一个线程后,它不应该基于陈旧值抛出异常.GetValue()如果值不为null,则表示不需要刷新值.我提出了几种实现这一目标的方法,但我不确定哪些是正确的,哪些是有效的,为什么它们(正确)和(有效),以及是否有更好的方法来实现我想要的东西.
Interlocked.CompareExchange写入场Interlocked.CompareExchange从外地来读Interlocked.CompareExchange(ref v, null, null)对字段执行操作后将导致下一次访问获得的值至少与Interlocked.CompareExchange看到的值相同.代码:
public class SetOnce1<T> where T : class
{
private T _value = null;
public T GetValue() {
if (_value == null) {
// Maybe we got a stale value (from the cache or compiler optimization).
// Read an …Run Code Online (Sandbox Code Playgroud) 我尝试使用ForkJoinPool 来并行化我的CPU密集型计算.我对ForkJoinPool的理解是,只要任何任务可以执行,它就会继续工作.不幸的是,我经常观察工作线程空闲/等待,因此并非所有CPU都保持忙碌状态.有时我甚至观察到额外的工作线程.
我没想到这一点,因为我严格尝试使用非阻塞任务.我的观察非常类似于ForkJoinPool似乎浪费了一个线程.在对ForkJoinPool进行了大量调试之后我猜了一下:
我使用invokeAll()在子任务列表上分配工作.在invokeAll()完成后执行第一个任务本身,它开始加入其他任务.这很好,直到下一个要连接的任务位于执行队列之上.不幸的是,我提交了异步的其他任务而没有加入它们.我期望ForkJoin框架首先继续执行这些任务,然后再转回加入任何剩余的任务.
但它似乎不是这样工作的.相反,工作线程停止调用wait()直到等待的任务准备好(可能是由其他工作线程执行).我没有验证这一点,但似乎是调用join()的一般缺陷.
ForkJoinPool提供了一个asyncMode,但这是一个全局参数,不能用于单个提交.但我喜欢看到我的异步分叉任务很快就会被执行.
那么,为什么ForkJoinTask.doJoin()不是简单地在其队列之上执行任何可用任务,直到它准备好(由自己执行或被其他人窃取)?
我使用AtomicReference来实现AtomicInteger.然而,在测试中,我注意到即使在单线程环境中,一旦其值达到128,CAS操作就会卡住.我做错了什么或在AtomicReference中有一个警告(可能与CPU有关)?这是我的代码:
public class MyAtomInt {
private final AtomicReference<Integer> ref;
public MyAtomInt(int init) {
ref = new AtomicReference<Integer>(init);
}
public MyAtomInt() {
this(0);
}
public void inc() {
while (true) {
int oldVal = ref.get();
int nextVal = oldVal + 1;
boolean success = ref.compareAndSet(oldVal, nextVal); // false once oldVal = 128
if (success) {
return;
}
}
}
public int get() {
return ref.get();
}
static class Task implements Runnable {
private final MyAtomInt myAtomInt;
private final int incCount;
public …Run Code Online (Sandbox Code Playgroud) 我正在阅读Anthony Williams的C++ Concurrency in Action,并且不了解它push的lock_free_stack类实现.
为什么原子load不在while循环中呢?他给出的理由是:
因此,您不必每次都通过循环重新加载头,因为编译器会为您执行此操作.
但我不明白.有人可以对此有所了解吗?
template<typename T>
class lock_free_stack
{
private:
struct node
{
T data;
node* next;
node(T const& data_) :
data(data_)
{}
};
std::atomic<node*> head;
public:
void push(T const& data)
{
node* const new_node=new node(data);
new_node->next=head.load();
while(!head.compare_exchange_weak(new_node->next,new_node));
}
};
Run Code Online (Sandbox Code Playgroud) 在" C++ Concurrency in Action"一书中,作者给出了使用危险指针实现无锁堆栈数据结构的示例.部分代码如下:
std::shared_ptr<T> pop()
{
std::atomic<void*>& hp=get_hazard_pointer_for_current_thread();
node* old_head=head.load();
node* temp;
do
{
temp=old_head;
hp.store(old_head);
old_head=head.load();
} while(old_head!=temp);
// ...
}
Run Code Online (Sandbox Code Playgroud)
描述说
您必须在
while循环中执行此操作,以确保node在读取旧head指针和危险指针设置之间未删除.在此窗口期间,没有其他线程知道您正在访问此特定节点.幸运的是,如果head要删除旧 节点,head它本身必须已更改,因此您可以检查并保持循环,直到您知道head指针仍然具有您设置危险指针的相同值.
我认为代码存在缺陷,因为head节点受ABA问题的影响.即使值head保持不变,它最初指向的节点也可能已被删除.head分配了一个新节点,该节点恰好具有与前一个节点相同的地址值.