www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > > 充電吧
[導讀]AtomicPointer 是 leveldb 提供的一個原子指針操作類,使用了基于原子操作(atomic operation)或者內(nèi)存屏障(memory barrier)的同步訪問機制,這比用鎖和信

AtomicPointer 是 leveldb 提供的一個原子指針操作類,使用了基于原子操作(atomic operation)或者內(nèi)存屏障(memory barrier)的同步訪問機制,這比用鎖和信號量的效率要高。

一.Windows版本的AtomicPointer實現(xiàn)

先上源碼,這個是Windows版本的源碼,源文件位置:leveldb/port/port_win.h和leveldb/port/port_win.cc

class AtomicPointer {
 private:
  void * rep_;
 public:
  AtomicPointer() : rep_(nullptr) { }
  explicit AtomicPointer(void* v); 
  void* Acquire_Load() const;
  void Release_Store(void* v);
  void* NoBarrier_Load() const;
  void NoBarrier_Store(void* v);
};
AtomicPointer::AtomicPointer(void* v) {
  Release_Store(v);
}
// 使用原子操作的方式讀取,即同步的讀操作
void* AtomicPointer::Acquire_Load() const {
  void * p = nullptr;
  InterlockedExchangePointer(&p, rep_);
  return p;
}
// 使用原子操作的方式寫入,即同步的寫操作
void AtomicPointer::Release_Store(void* v) {
  InterlockedExchangePointer(&rep_, v);
}
// 不使用原子操作的方式讀取,即不同步的讀操作
void* AtomicPointer::NoBarrier_Load() const {
  return rep_;
}
// 不使用原子操作的方式寫入,即不同步的寫操作
void AtomicPointer::NoBarrier_Store(void* v) {
  rep_ = v;
}
從代碼中可以看出,AtomicPointer是基于原子操作實現(xiàn)的一個原子指針操作類,通過原子操作實現(xiàn)多線程的讀寫同步。原子操作,即不可分割開的操作。該操作一定是在同一個CPU時間片中完成,這樣即使線程被切換,多個線程也不會看到同一塊內(nèi)存中不完整的數(shù)據(jù)。

這里同步?jīng)]有用到鎖,所以涉及到了無鎖編程(Lock-Free)的概念。

二.無鎖編程

無鎖編程具體使用和考慮到的技術(shù)方法包括:原子操作(atomic operation)、內(nèi)存柵欄(memory barrier)、內(nèi)存順序沖突(memory order)、 指令序列一致性(sequential consistency)等等。之所以會出現(xiàn)無鎖編程技術(shù),因為基于鎖的編程的有如下缺點。
多線程編程是多CPU(多核CPU或者多個CPU)系統(tǒng)在中應(yīng)用最廣泛的一種編程方式,在傳統(tǒng)的多線程編程中,多線程之間一般用各種鎖的機制來保證正確的對共享資源(share resources)進行訪問和操作。在多線程編程中只要需要共享某些數(shù)據(jù),就應(yīng)當將對它的訪問串行化。比如像++count(count是整型變量)這樣的簡單操作也得加鎖,因為即便是增量操作這樣的操作,在匯編代碼中實際上也是分三步進行的:讀、改、寫(回)。
movl x, %eax
addl $1, %eax
movl %eax, x
更進一步,甚至內(nèi)存變量的賦值操作都不能保證是原子的,比如在32位環(huán)境下運行這樣的函數(shù)
void setValue()?
{?
? ? ?value = 0x100000006;?
}
所有的C/C++操作被認定為非原子的。執(zhí)行的過程中,這兩條指令之間也是可以被打斷的,而不是一條原子操作(也就是所謂的寫撕裂),所以修改共享數(shù)據(jù)的操作必須以原子操作的形式出現(xiàn),這樣才能保證沒有其它線程能在中途插一腳來破壞相應(yīng)數(shù)據(jù)。
而在使用鎖機制的過程中,即便在鎖的粒度(granularity)、負載(overhead)、競爭(contention)、死鎖(deadlock)等需要重點控制的方面解決的很好,也無法徹底避免這種機制的如下一些缺點:
1、鎖機制會引起線程的阻塞(block),對于沒有能占用到鎖的線程或者進程,將一直等待到鎖的占有者釋放鎖資源后才能繼續(xù)執(zhí)行,而等待時間理論上是不可設(shè)置和預(yù)估的。

2、申請和釋放鎖的操作,增加了很多訪問共享資源的消耗,尤其是在鎖競爭(lock-contention)很嚴重的時候,比如這篇文章所說

Locks Aren't Slow; Lock Contention Is

3、現(xiàn)有實現(xiàn)的各種鎖機制,都不能很好的避免編程開發(fā)者設(shè)計實現(xiàn)的程序出現(xiàn)死鎖或者活鎖的可能
4、優(yōu)先級反轉(zhuǎn)(prorithy inversion)和鎖護送(Convoying)的現(xiàn)象
5、難以調(diào)試
無鎖編程(Lock-Free)就是在某些應(yīng)用場景和領(lǐng)域下解決以上基于鎖機制的并發(fā)編程的一種方案。
無鎖編程的概念做一般應(yīng)用層開發(fā)的會較少接觸到,因為多線程的時候?qū)蚕碣Y源的操作一般是用鎖來完成的。鎖本身對這個任務(wù)完成的很好,但是存在性能的問題,也就是在對性能要求很高的,高并發(fā)的場景下,鎖會帶來性能瓶頸。所以在一些如數(shù)據(jù)庫這樣的應(yīng)用或者linux 內(nèi)核里經(jīng)常會看到一些無鎖的并發(fā)編程。

鎖是一個高層次的接口,隱藏了很多并發(fā)編程時會出現(xiàn)的非常古怪的問題。當不用鎖的時候,就要考慮這些問題。主要有兩個方面的影響:編譯器對指令的排序和cpu對指令的排序。它們排序的目的主要是優(yōu)化和提高效率。排序的原則是在單核單線程下最終的效果不會發(fā)生改變。單核多線程的時候,編譯器的亂序就會帶來問題,多核的時候,又會涉及cpu對指令的亂序。memory-ordering-at-compile-time和memory-reordering-caught-in-the-act里提到了亂序?qū)е碌膯栴}。

除了Windows版本,源碼中還提供了AtomicPointer的另外兩種實現(xiàn)。源文件位置:leveldb/port/atomic_pointer.h?
三.利用std::atomic實現(xiàn)AtomicPointer
std::atomic是C++11提供的原子模板類,std::atomic對int、char、 bool等數(shù)據(jù)類型進行原子性封裝。原子類型對象的主要特點就是從不同線程訪問不會導致數(shù)據(jù)競爭(data race)。從不同線程訪問某個原子對象是良性 (well-defined) 行為,而通常對于非原子類型而言,并發(fā)訪問某個對象(如果不做任何同步操作)會導致未定義 (undifined) 行為發(fā)生。因此使用std::atomic可實現(xiàn)數(shù)據(jù)同步的無鎖設(shè)計。
#if defined(LEVELDB_CSTDATOMIC_PRESENT)
class AtomicPointer {
 private:
  std::atomic rep_;
 public:
  AtomicPointer() { }
  explicit AtomicPointer(void* v) : rep_(v) { }
  inline void* Acquire_Load() const {
    return rep_.load(std::memory_order_acquire);
  }
  inline void Release_Store(void* v) {
    rep_.store(v, std::memory_order_release);
  }
  inline void* NoBarrier_Load() const {
    return rep_.load(std::memory_order_relaxed);
  }
  inline void NoBarrier_Store(void* v) {
    rep_.store(v, std::memory_order_relaxed);
  }
};
#endif
四.利用內(nèi)存屏障來實現(xiàn)AtomicPointer
// Define MemoryBarrier() if available
// Windows on x86
#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)
// windows.h already provides a MemoryBarrier(void) macro
// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx
#define LEVELDB_HAVE_MEMORY_BARRIER


// Gcc on x86
#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__)
inline void MemoryBarrier() {
  // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on
  // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.
  __asm__ __volatile__("" : : : "memory");
}
#define LEVELDB_HAVE_MEMORY_BARRIER


// Sun Studio
#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC)
inline void MemoryBarrier() {
  // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on
  // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.
  asm volatile("" : : : "memory");
}
#define LEVELDB_HAVE_MEMORY_BARRIER


// Mac OS
#elif defined(OS_MACOSX)
inline void MemoryBarrier() {
  OSMemoryBarrier();
}
#define LEVELDB_HAVE_MEMORY_BARRIER


// ARM
#elif defined(ARCH_CPU_ARM_FAMILY)
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
    (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
inline void MemoryBarrier() {
  pLinuxKernelMemoryBarrier();
}
#define LEVELDB_HAVE_MEMORY_BARRIER
#endif


// AtomicPointer built using platform-specific MemoryBarrier()
#if defined(LEVELDB_HAVE_MEMORY_BARRIER)
class AtomicPointer {
 private:
  void* rep_;
 public:
  AtomicPointer() { }
  explicit AtomicPointer(void* p) : rep_(p) {}
  inline void* NoBarrier_Load() const { return rep_; }
  inline void NoBarrier_Store(void* v) { rep_ = v; }
  inline void* Acquire_Load() const {
    void* result = rep_;
    MemoryBarrier();
    return result;
  }
  inline void Release_Store(void* v) {
    MemoryBarrier();
    rep_ = v;
  }
};
#endif
從上面可以看出各個平臺都有相應(yīng)的MemoryBarrier()實現(xiàn),比如說windows平臺已經(jīng)定義過MemoryBarrier(void)宏,可以直接使用。而linux平臺的gcc則通過內(nèi)聯(lián)一條匯編指令__asm__ __volatile__("" : : : "memory");來自定義MemoryBarrier()。
MemoryBarrier()的作用是添加內(nèi)存屏障,當這個MemoryBarrier()之前的代碼修改了某個變量的內(nèi)存值后,其他 CPU 和緩存 (Cache) 中的該變量的值將會失效,必須重新從內(nèi)存中獲取該變量的值。
內(nèi)存屏障的基本用途是避免編譯器優(yōu)化指令 。有些編譯器默認會在編譯期間對代碼進行優(yōu)化,從而改變匯編代碼的指令執(zhí)行順序,如果你是在單線程上運行可能會正常,但是在多線程環(huán)境很可能會發(fā)生問題(如果你的程序?qū)χ噶畹膱?zhí)行順序有嚴格的要求)。而內(nèi)存屏障就可以阻止編譯器在編譯期間優(yōu)化我們的指令順序,為你的程序在多線程環(huán)境下的正確運行提供了保障,但是不能阻止 CPU 在運行時重新排序指令。
舉個例子,有下面的代碼:
a = b = 0;
//thread1
a = 1
b = 2

//thread2
if (b == 2) {
   //這時a是1嗎?
}
假設(shè)只有單核單線程1的時候,由于a和 b的賦值沒有關(guān)系,因此編譯器可能會先賦值b然后賦值a,注意單線程的情況下是沒有問題的,但是如果還有線程2,那么就不能保證線程2看到b為2 的時候a就為1。再假設(shè)線程1改為如下的代碼:
a = 1
complier_fence()
b = 2
其中complier_fence()為一條阻止編譯器在fence前后亂序的指令,x86/64下可以是下面的匯編語句,也可以由其他語言提供的語句保證。asm volatile(“” ::: “memory”);此時我們能保證b的賦值一定發(fā)生在a賦值之后。那么此時線程2的邏輯是對的嗎?還不能保證。因為線程2可能會先讀取a的舊值,然后再讀取b的值。從編譯器來看a和b之間沒有關(guān)聯(lián),因此這樣的優(yōu)化是可能發(fā)生的。所以線程2也需要加上編譯器級的屏障。
if (b == 2) {
   complier_fence()
   //這時a是1嗎?
}
加上了這些保證,編譯器輸出的指令能確保a,b之間的順序性。注意a,b的賦值也可以換成更復(fù)雜的語句,屏障保證了屏障之前的讀寫一定發(fā)生在屏障之后的讀寫之前,但是屏障前后內(nèi)部的原子性和順序性是沒有保證的。
當把這樣的程序放到多核的環(huán)境上運行的時候,a,b賦值之間的順序性又沒有保證了。這是由于多核CPU在執(zhí)行編譯器排序好的指令的時候還是會亂序執(zhí)行。這個問題在memory-barriers-are-like-source-control-operations里有很好的解釋。這里不再多說。

同樣的,為了解決這樣的問題,語言上有一些語句提供屏障的效果,保證屏障前后指令執(zhí)行的順序性。而且,慶幸的是,一般能保證CPU內(nèi)存屏障的語句也會自動保證編譯器級的屏障。注意,不同的CPU的內(nèi)存模型(即對內(nèi)存中的指令的執(zhí)行順序如何進行的模型)是不一樣的,很辛運的,x86/64是的內(nèi)存模型是強內(nèi)存模型,它對CUP的亂序執(zhí)行的影響是最小的。

A strong hardware memory model is one in which every machine instruction comes implicitly withacquire and release semantics. As a result, when one CPU core performs a sequence of writes, every other CPU core sees those values change in the same order that they were written.

因此在x86/64上可以不用考慮CPU的內(nèi)存屏障,只需要在必要的時候考慮編譯器的亂序問題即可。

回到leveldb里的AtomicPointer,注意到其中幾個成員函數(shù)都是inline,如果不是inline,其實沒有必要加上內(nèi)存屏障,因為函數(shù)能夠提供很強的內(nèi)存屏障保證。下面這段話摘自memory-ordering-at-compile-time:

In fact, the majority of function calls act as compiler barriers, whether they contain their own compiler barrier or not. This excludes inline functions, functions declared with thepure attribute, and cases where link-time code generation is used. Other than those cases, a call to an external function is even stronger than a compiler barrier, since the compiler has no idea what the function’s side effects will be. It must forget any assumptions it made about memory that is potentially visible to that function.

下面針對Acquire_Load和Release_Store假設(shè)一個場景:
//thread1
Object.var1 = a;
Object.var2 = b;
Object.var2 = c;
atomicpointer.Release_Store(p);

//thread2
user_pointer = atomicpointer.Acquire_Load();
get Object.var1
get Object.var2
get Object.var3

對于Store Barrier來說,在指令后插入Store Barrier,能讓寫入緩存中的最新數(shù)據(jù)更新寫入主內(nèi)存,讓其他線程可見。

對于Load Barrier來說,在指令前插入Load Barrier,可以讓高速緩存中的數(shù)據(jù)失效,強制從新從主內(nèi)存加載數(shù)據(jù)。

注意acquire,release模型適合單生產(chǎn)者和單消費者的模型,如果有多個生產(chǎn)者,那么現(xiàn)有的保障是不足的,會涉及到原子性的問題。


參考鏈接:

Locks Aren't Slow; Lock Contention Is

memory-ordering-at-compile-time

memory-reordering-caught-in-the-act

An Introduction to Lock-Free Programming

Acquire and Release Fences

Memory Barriers Are Like Source Control Operations

Acquire and Release Semantics

并發(fā)編程系列之一:鎖的意義

理解Memory Barrier(內(nèi)存屏障)
本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應(yīng)用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉