标签: lockless

睡眠(0)和暂停指令的繁忙循环有什么不同?

我想在我的应用程序中等待一个应该立即发生的事件,所以我不想让我的线程等待并稍后唤醒它。我想知道使用Sleep(0)和硬件暂停指令有什么区别。

我看不到以下程序的 CPU 利用率有任何差异。我的问题不是关于省电的考虑。

#include <iostream>
using namespace std;
#include <windows.h>

bool t = false;
int main() {
       while(t == false)
       {
              __asm { pause } ;
              //Sleep(0);
       }
}
Run Code Online (Sandbox Code Playgroud)

c++ x86 hardware-interface busy-loop lockless

6
推荐指数
2
解决办法
1925
查看次数

Go的缓冲通道是否无锁?

Go的缓冲通道本质上是一个线程安全的FIFO队列.(请参阅是否可以将Go的缓冲通道用作线程安全队列?)

我想知道它是如何实现的.它是无锁的,如有多个读或写线程的无锁队列吗?

Go的src目录(grep -r Lock .|grep chan)中的greping 给出了以下输出:

./pkg/runtime/chan.c:   Lock;
./pkg/runtime/chan_test.go: m.Lock()
./pkg/runtime/chan_test.go: m.Lock() // wait
./pkg/sync/cond.go: L Locker // held while observing or changing the condition
Run Code Online (Sandbox Code Playgroud)

但是不要锁定我的机器(MacOS,intel x86_64).有没有官方资源来验证这个?

thread-safety go lockless

6
推荐指数
2
解决办法
921
查看次数

读者/写者锁...没有读者锁?

我感觉这可能是一种非常普遍和常见的情况,对此有一个众所周知的无锁解决方案。

简而言之,我希望有一种像读取器/写入器锁这样的方法,但这不需要读取器获取锁,因此可以获得更好的平均性能。

相反,对于读取器来说会有一些原子操作(128 位 CAS),对于写入器来说会有一个互斥锁。我有数据结构的两个副本,一个用于正常成功查询的只读副本,以及一个要在互斥锁保护下更新的相同副本。将数据插入可写副本后,我们将其设为新的可读副本。一旦所有待处理的读取器都读完它,旧的可读副本就会被依次插入,写入器会旋转剩余的读取器数量,直到其为零,然后依次修改它,最后释放互斥体。

或类似的东西。

存在这样的东西吗?

c++ concurrency lock-free lockless stdatomic

6
推荐指数
2
解决办法
2740
查看次数

无锁的定义

存在三种不同类型的“无锁”算法。并发实践中给出的定义是:

\n
    \n
  1. 无阻碍:如果所有其他线程都暂停,则任何给定的\n线程都将在有限的步骤中完成其操作。
  2. \n
  3. 无锁:如果多个线程正在对一个数据结构进行操作,那么在有限数量的步骤之后,其中一个线程将完成其操作。
  4. \n
  5. 无等待:每个操作数据结构的线程都将在有限的步骤中完成其操作,即使其他线程也在操作该数据结构。
  6. \n
\n

Herb Sutter 在他的演讲《无锁编程》中说道:

\n
\n

非正式地,“无锁”\xe2\x89\x88“不使用互斥体”==其中任何一个。

\n
\n

我不明白为什么基于锁的算法不能落入上面给出的无锁定义。这是一个简单的基于锁的程序:

\n
#include <iostream>\n#include <mutex>\n#include <thread>\n\nstd::mutex x_mut;\n\nvoid print(int& x) {\n    std::lock_guard<std::mutex> lk(x_mut);\n    std::cout << x;\n}\n\nvoid add(int& x, int y) {\n    std::lock_guard<std::mutex> lk(x_mut);\n    x += y;\n}\n\nint main() {\n\n    int i = 3;\n\n    std::thread thread1{print, std::ref(i)};\n\n    std::thread thread2(add, std::ref(i), 4);\n\n    thread1.join();\n\n    thread2.join();\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果这两个线程都在运行,那么在有限数量的步骤之后,其中一个必须完成。为什么我的程序不满足“无锁”的定义?

\n

c++ multithreading computer-science lock-free lockless

6
推荐指数
1
解决办法
1383
查看次数

无锁队列实现最终会在压力下产生循环

我有一个用C语言编写的无锁队列,它是一个链表,包含发布到单个线程并在单个线程中处理的多个线程的请求.经过几个小时的压力后,我最终得到了最后一个请求的下一个指针指向自身,这会创建一个无限循环并锁定处理线程.

应用程序在Linux和Windows上运行(并失败).我在Windows上调试,我的COMPARE_EXCHANGE_PTR地图是InterlockedCompareExchangePointer.

这是将请求推送到列表头部的代码,并从多个线程调用:

void push_request(struct request * volatile * root, struct request * request)
{
    assert(request);

    do {
        request->next = *root;
    } while(COMPARE_EXCHANGE_PTR(root, request, request->next) != request->next);
}
Run Code Online (Sandbox Code Playgroud)

这是从列表末尾获取请求的代码,仅由处理它们的单个线程调用:

struct request * pop_request(struct request * volatile * root)
{
    struct request * volatile * p;
    struct request * request;

    do {
        p = root;
        while(*p && (*p)->next) p = &(*p)->next; // <- loops here
        request = *p;
    } while(COMPARE_EXCHANGE_PTR(p, NULL, request) != request);

     assert(request->next == NULL); …
Run Code Online (Sandbox Code Playgroud)

c queue multithreading lockless

5
推荐指数
1
解决办法
1383
查看次数

线程安全的通用字段

我有一个通用字段和一个封装它的属性:

T item;

public T Item
{
    get { return item; }
    set { item = value; }
}
Run Code Online (Sandbox Code Playgroud)

问题是这个属性可以从一个线程写入并同时从多个线程读取.如果T是a struct,或者long,读者可能得到的结果是旧的价值和新的价值.我怎么能防止这种情况?

我尝试过使用volatile,但这是不可能的:

易失性字段不能是"T"类型.

由于这是一个更简单的代码,我已经编写过了,所以ConcurrentQueue<T>我也想过在这里使用它:

ConcurrentQueue<T> item;

public T Item
{
    get
    {
        T result;
        item.TryPeek(out result);
        return item;
    }

    set
    {
        item.TryEnqueue(value);
        T ignored;
        item.TryDequeue(out ignored);
    }
}
Run Code Online (Sandbox Code Playgroud)

这可行,但在我看来,这是一个过于简单的解决方案.

性能很重要,因此,如果可能,应避免锁定.

如果a set同时发生get,我不关心是get返回旧值还是新值.

.net c# thread-safety lockless

5
推荐指数
1
解决办法
689
查看次数

简单的无锁秒表

根据 MSDN,类Stopwatch实例方法对于多线程访问并不安全。这也可以通过检查个别方法来确认。

然而,由于我只需要在代码中的几个地方使用简单的“经过时间”计时器,我想知道它是否仍然可以无锁地完成,使用类似的东西:

public class ElapsedTimer : IElapsedTimer
{
    /// Shared (static) stopwatch instance.
    static readonly Stopwatch _stopwatch = Stopwatch.StartNew();

    /// Stopwatch offset captured at last call to Reset
    long _lastResetTime;

    /// Each instance is immediately reset when created
    public ElapsedTimer()
    { 
        Reset();
    }

    /// Resets this instance.
    public void Reset()
    {
        Interlocked.Exchange(ref _lastResetTime, _stopwatch.ElapsedMilliseconds);
    }

    /// Seconds elapsed since last reset.
    public double SecondsElapsed
    {
        get
        {
             var resetTime = Interlocked.Read(ref _lastResetTime);
             return (_stopwatch.ElapsedMilliseconds - resetTime) / …
Run Code Online (Sandbox Code Playgroud)

c# atomic stopwatch lockless

5
推荐指数
1
解决办法
723
查看次数

如何用c ++ 11 CAS实现ABA计数器?

我正在实现一个基于此算法的无锁队列,该算法使用计数器来解决ABA问题.但我不知道如何用c ++ 11 CAS实现这个计数器.例如,从算法:

E9:    if CAS(&tail.ptr->next, next, <node, next.count+1>)
Run Code Online (Sandbox Code Playgroud)

它是一个原子操作,意思是如果tail.ptr->next等于next,则tail.ptr->next指向node同时(原子地)产生next.count+1.但是,使用C++ 11 CAS,我只能实现:

std::atomic_compare_exchange_weak(&tail.ptr->next, next, node);
Run Code Online (Sandbox Code Playgroud)

这不可能next.count+1同时发生.

c++ multithreading lock-free lockless c++11

5
推荐指数
1
解决办法
1924
查看次数

高速缓存行填充,用于变量是高速缓存行大小的倍数

我正在创建一个非常快速的多线程离散事件模拟框架。该框架的核心使用原子和无锁编程技术来实现跨多个线程的快速执行。这要求我将一些变量对齐到高速缓存行并填充剩余的高速缓存行空间,以便没有高速缓存行争用。这是我的方法:

// compute cache line padding size
constexpr u64 CLPAD(u64 _objSize) {
  return ((_objSize / CACHELINE_SIZE) * CACHELINE_SIZE) +
      (((_objSize % CACHELINE_SIZE) > 0) * CACHELINE_SIZE) -
      _objSize;
}

alignas(CACHELINE_SIZE) MyObject myObj;
char padding[CLPAD(sizeof(myObj))];
Run Code Online (Sandbox Code Playgroud)

这对我来说很棒,但是今天当我将这种方法用于新的对象类型时,我偶然发现了一个问题。CLPAD()函数返回将输入类型填充到下一个缓存行所需的字符数。但是,如果我输入的大小恰好是高速缓存行数的倍数,则CLPAD返回0。如果尝试创建大小为零的数组,则会收到以下警告/错误:

ISO C++ forbids zero-size array 'padding'
Run Code Online (Sandbox Code Playgroud)

我知道在这种情况下可以修改CLPAD()以返回CACHELINE_SIZE,但是随后我无缘无故地烧写了一个缓存行空间。

如果CLPAD返回0,如何使“ padding”的声明消失?

c++ simulation multithreading caching lockless

5
推荐指数
1
解决办法
544
查看次数

在具有相同顺序的原子加载/存储之前使用 std::atomic_thread_fence 总是多余的吗?

鉴于:

std::atomic<uint64_t> b;

void f()
{
    std::atomic_thread_fence(std::memory_order::memory_order_acquire);

    uint64_t a = b.load(std::memory_order::memory_order_acquire);

    // code using a...
}
Run Code Online (Sandbox Code Playgroud)

去掉这个调用会有std::atomic_thread_fence什么影响吗?如果有的话有一个简洁的例子吗?请记住,其他函数可能会存储/加载b并调用f.

c++ multithreading atomic lockless instruction-reordering

5
推荐指数
1
解决办法
455
查看次数