无锁和轻量级机制同时处理双数据类型

时间:2017-02-07 09:53:00

标签: c++ multithreading visual-c++ concurrency c++14

#include <windows.h>
#include <iostream>
DWORD tID;
volatile double fps;

DWORD  WINAPI ThreadFunc(LPVOID param)
{

    while (1)
    {
         //Need lock-free solution here
        std::cout << GetCurrentThreadId() << " Thread:  " << fps << std::endl;      
        Sleep(1);
    }
    return 1;
}

void mainFunc(const double& pGps)
{
    //Perormance critical function - should be lightwieght as possible as it can
    fps = pGps; // need lock-free efficient solution
}

int main()
{
    double gps, pGps =0.0;
    auto fb1 = CreateThread(NULL, NULL, &ThreadFunc, &gps,0,&tID);
    while (1)
    {
        pGps = pGps + 1;
        mainFunc(pGps);
        Sleep(1);
    }
    system("Pause");
    return 0;
}

我正在使用Visual C ++编译器。 这里 fps 双变量在线程和 fb1 线程之间共享,我需要在良好同步的机制中建立并发写和读访问。这里只需要考虑两个线程。主线程 - 制作人 (作家),fb1线程将是 消费者 (读者)。

mainFunc应该是轻量级的

这就是问题, mainFunc 应该更轻量级,更高效(指令数量更少)。我尝试了使用std::atomic<double> fps,Win32 Interlocked操作的不同方法,但我无法#39;达到预期的性能。 首先,我尝试使用 Slim R / W 锁定,但解决方案应该是无锁机制,即使SRW锁定没有提供所需解决方案的性能。

如果我们使用无锁数据结构,它会是一个有效的解决方案吗?

请通过引入无锁数据结构检查以下修改后的代码

DWORD tID,tID1;
volatile double fps;

LK_Free_DataStructre datStruct;
DWORD  WINAPI ThreadFunc(LPVOID param)
{
        while (1)
        {
            size_t n = datStruct.size();
            for (auto i = 0; i < n; ++i)
            {
                std::cout << GetCurrentThreadId() << " Thread:  " << datStruct.get(i) << std::endl;
            }
            Sleep(1);
        }   
    return 1;
}

void mainFunc(double& pGps)
{
    datStruct.insert(pGps);
}

int main()
{
    double gps, pGps =0.0;
    auto fb1 = CreateThread(NULL, NULL, &ThreadFunc, &gps,0,&tID);
    while (1)
    {
        pGps = pGps + 1;
        mainFunc(pGps);
        Sleep(1);
    }

    system("Pause");
    return 0;
}

假设我们必须定义 无锁 轻量级 数据结构 - 这是 LK_Free_DataStructre 即可。 那么构建数据结构中最合适的 Win32 C / C ++ 是什么,可以取代 LK_Free_DataStructre

推荐std::atomic<double>吗?

2 个答案:

答案 0 :(得分:1)

在这种情况下,最简单,最快速的答案是std::atomic<double> - 它基本上是为此而设计的。你只需要避免std :: memory_order_seq_cst强加的内存栅栏(默认的内存顺序)。像下面这样的东西根本没有同步,只是原子性(一次性移动一个双倍的记忆),所以几乎和指定一个普通的双变量一样快,同时仍然是安全的。

#include <atomic>

std::atomic<double> fps;


void setFps(double val) {
  fps.store(val, std::memory_order_relaxed);
}

double getFps() {
  return fps.load(std::memory_order_relaxed);
}

如果你使用无锁数据结构(例如队列),那么

  • 如果作家过快,它会填满并可能阻止
  • 会慢很多

所以我会避免这种情况。

微软的联锁操作与std :: atomic基本相同,只是非标准且难以使用(IMO),所以我也会避免这种情况。

答案 1 :(得分:0)

  

这里只需要考虑两个线程。主线程 - 生产者(编写者),fb1线程将是消费者(Reader)。

听起来你需要的是单生产者/单一消费者队列。

您可能希望查看Boost Single-Producer/Single-Consumer Queue用法示例。

单生产者/单一消费者队列的好处在于它可以以无等待的方式实现(除了无锁)。也就是说,pushpop操作在固定数量的指令中完成,不会涉及繁忙的等待。