天天看點

多線程如何實作高性能計數器(無鎖)

多線程協作免不了使用計數器,

通常的代碼,c++一般會使用鎖,或者原子變量操作:

std::mutex mutexCounter;
int count;


void add()
{
    std::lock_guard<std::mutex> guard(mutexCounter);
    count ++;
}
           
std::atomic<int> count;

void add()
{

    count ++;
}
           

在性能要求比較高的情況下,這樣的代碼100%是整個業務的瓶頸;

那麼如何優化?答案是讓線程寫到自己的計數内,在計算總和時候再使用鎖來彙總一次;

方法一:

可以每個線程配置設定一個0-N (64個左右)的索引号,通過索引去通路計數單元中的對應的的槽位,這樣線程之間不會互相影響,就實作了無鎖寫入;讀的時候可以根據對精度的要求來确定是否需要加鎖同步統計:

#pragma once
#include <thread>
#include <mutex>
#include <map>
#include <vector>

class VarBase
{
public:
	static int getIndex();

private:
	static int cusor;
	static thread_local int index;
	//static std::map<std::thread::id, int> idMap;
	static std::mutex mutexMap;

};

class VarSingle
{
public:
	VarSingle() : data(64)
	{

	}

	inline int setData(int val)
	{
		int index = VarBase::getIndex();
		if (index >= data.size())
		{
			std::lock_guard<std::mutex> gurad(mutexVec);
			if (index >= data.size())
				data.resize(data.size() + 16);
		}
		data[index] = val;
		return index;
	}

	int getVal()
	{
		int index = VarBase::getIndex();
		if (index >= data.size())
			return 0;

		return data[index];
	}

	int64_t getSum(bool precise)
	{
		int64_t all = 0;
		if (precise)
		{
			std::lock_guard<std::mutex> gurad(mutexVec);
			for (size_t i = 0; i < data.size(); i++)
			{
				all += data[i];
			}
		}
		else
		{
			for (size_t i = 0; i < data.size(); i++)
			{
				all += data[i];
			}
		}

		return all;
	}

	void reset()
	{
		std::lock_guard<std::mutex> gurad(mutexVec);
		for (size_t i = 0; i < data.size(); i++)
		{
			data[i] = 0;
		}
	}

private:
	std::vector<int> data;
	std::mutex mutexVec;
};


// Var.cpp
#include "Var.h"


int VarBase::index = -1;
int VarBase::cusor = 0;
int VarBase::getIndex()
{
	if (index == -1)
	{
		std::lock_guard<std::mutex>  guard(mutexMap);
		index = cusor++;
		
	}

	return index;
}
           

第二種:

可以設定全局的一個map資訊,

Var的構造函數,将自己的線程局部存儲的指針注冊到全局資訊中,在析構時候删除注冊資訊;

這樣,每次寫資料都寫到線程本地存儲中,而統計時候通過全局注冊資訊彙總;

代碼,略;

繼續閱讀