1. 程式人生 > >單生產者單消費者的環形佇列

單生產者單消費者的環形佇列

環形佇列設計如下:

template <class T>
class ring
{
puclic:
    explicit ring(int size): m_maxsize(size), m_rp(0), m_wp(0)
    {   
        if (size < 2)
        {
            throw std::runtime_error("too few elements, size must grater than 1");
        }
        m_array = new T[size+1];
    }
    
    ~ring
    {
        delete [] m_array;
    }
    bool full()const
    {
        return inc(wp) == rp;
    }


    bool empty()const
    {
        return rp == wp;
    }


    int inc(int n)const
    {
        return ++n % (maxsize+1);
    }


    int push_back(const T& v)
    {
       if (full()) 
        {
            return -1;
        }
        m_array[m_wp] = v;
        m_wp = inc(m_wp);
        return 0;
    }
    
    int pop_front(T& v)
    {
        if (empty())
        {
            return -1;
        }
        v = m_array[m_rp];
        m_rp = inc(m_rp);
        return 0;
    }
    
    size_t size()const
    {   
        size_t n = m_wp - m_rp;
        return n > 0 ? n : m_maxsize + n;
    }
    
private:
    T* m_array;
    int m_maxsize;
    int m_rp __attribute__((align(8)));
    int m_wp __attribute__((align(8)));
    
}




考慮如下情況,單生產者,單消費者
1.讀執行緒追趕寫執行緒,但empty()條件rp == wp一直為true,即使不加鎖也執行緒安全
2.寫執行緒追趕讀執行緒,單full()條件wp++ == rp一直
3.極端情況下
rp=wp=m_maxsize
讀寫執行緒按如下指令執行:
寫執行緒                      讀執行緒
wp++
                            empty() rp == wp 為false
                            rp++
                            rp = 0   rp翻轉
                            empty()為false 可讀
wp = 0 wp翻轉
                            rp++    rp=1,這個時候讀索引已經在寫索引前,資料讀取異常
                            
4.所以rp,wp的操作必須是原子的,根據Intel手冊8.1.1節的介紹:


從Intel486 processor開始,以下的基本記憶體操作是原子的:
• Reading or writing a byte(一個位元組的讀寫)
• Reading or writing a word aligned on a 16-bit boundary(對齊到16位邊界的字的讀寫)
• Reading or writing a doubleword aligned on a 32-bit boundary(對齊到32位邊界的雙字的讀寫)


從Pentium processor開始,除了之前支援的原子操作外又新增了以下原子操作:
• Reading or writing a quadword aligned on a 64-bit boundary(對齊到64位邊界的四字的讀寫)
• 16-bit accesses to uncached memory locations that fit within a 32-bit data bus(未快取且在32位資料匯流排範圍之內的記憶體地址的訪問)


所以__attribute__((align(8)))可保證索引讀寫的原子性


 

相關推薦

生產者消費者環形佇列

環形佇列設計如下: template <class T> class ring { puclic:     explicit ring(int size): m_maxsize(size), m_rp(0), m_wp(0)     {            i

併發無鎖佇列學習(生產者消費者模型)

1、引言 本文介紹單生產者單消費者模型的佇列。根據寫入佇列的內容是定長還是變長,分為單生產者單消費者定長佇列和單生產者單消費者變長佇列兩種。單生產者單消費者模型的佇列操作過程是不需要進行加鎖的。生產者通過寫索引控制入隊操作,消費者通過讀索引控制出佇列操作。二者

併發無鎖佇列學習之二【生產者消費者

1、前言   最近工作比較忙,加班較多,每天晚上回到家10點多了。我不知道自己還能堅持多久,既然選擇了就要做到最好。寫部落格的少了。總覺得少了點什麼,需要繼續學習。今天繼續上個開篇寫,介紹單生產者單消費者模型的佇列。根據寫入佇列的內容是定長還是變長,分為單生產者單消費者定長佇列和單生產者單消費者變長佇列兩

19.執行緒同步:訊號量—>[生產者/消費者]單鏈表的插入和刪除

1.訊號量 1.訊號量本質 訊號量是鎖,是一種升級的mutex 訊號量在初始化時,可以指定共享資源的數量 2.相關函式 #include<semaphore.h> //標頭檔案 sem_t sem; //訊號量型別 int sem_destroy(se

基於單鏈表和基於環形佇列生產者消費者模型

先來介紹一下生產者消費者模型,舉一個常見的例子: 生活中,我們會經常去超市買東西,這裡涉及到了三個事物:我們、超市、供貨商。很容易就可以想到,我們就相當於消費者,而供貨商就相當於生產者,那麼超市就算是一個交易場所了。 對於生產者消費者模型我們可以簡單的總結一

生產者消費者無鎖佇列實現(c)

根據上面連結所說的原理實現的單生產者,單消費者無鎖佇列 bool __sync_bool_compare_and_swap (type *ptr, type oldval,type newval, ...) 函式提供原子的比較和交換,如果*ptr == oldval

一個取消多生產者消費者的日誌執行緒池服務

package concurrent._ThreadPool.logService; import net.jcip.annotations.GuardedBy; import org.omg.PortableInterceptor.SYSTEM_EXCEPTION; import java.io.

基於環形佇列生產者消費者模型

之前的Blog基於Queue的生產者消費者模型,已經談過了基於queue的生產消費模型,其空間是可以動態分配的。而現在是基於固定大小的環形佇列重寫這個程式。 基於環形佇列的生產消費模型 環形佇列採用陣列模擬,用模運算來模擬環狀特性 環形結構起始狀態和結束狀態都是一樣

生產者消費者模型(基於單鏈表、環形佇列、多執行緒、多消費多生產)

#include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <semaphore.h> #define SIZE 10 sem_t

基於單鏈表、環形佇列(併發有鎖)的多執行緒生產者消費者模型

基於單鏈表 基於環形佇列 1、環形緩衝區 緩衝區的好處,就是空間換時間和協調快慢執行緒。緩衝區可以用很多設計法,這裡說一下環形緩衝區的幾種設計方案,可以看成是幾種環形緩衝區的模式。設計環形緩衝區涉及到幾個點,一是超出緩衝區大小的的索引如何處理,

java簡單實現非同步佇列:使用生產者消費者模型

package com.yunshouhu; import java.util.concurrent.*; //java簡單實現非同步佇列:使用生產者與消費者模型 public class MyAsynQueue { // http://www.importnew.com/22519.h

activeMQ的建立生產者消費者的demo(佇列模式)

佇列模式流程圖(有點醜,網上查的) 程式碼實現生產者 String url="tcp://47.93.188.85:61616"; //目標名稱 String name="test"; //建立連線工廠 ConnectionFactory connectionFac

佇列實現生產者消費者

import threading,time,queue,randomdef scz(): while True: num=random.randint(1,100000) q.put(num)#放進佇列 print("生產者產生了%d資料" %(num)) time.sleep(5) q.task_do

1.【RabbitMQ】生產者消費者,通道,佇列,交換器和繫結

瞭解訊息通訊中的一些重點概念對於深化對RabbitMQ的理解有重要的意義;下面從生產者,消費者,通道,佇列,交換器和繫結,來介紹他們在訊息通訊過程中的角色和作用; 生產者:   建立訊息,然後釋出到代理伺服器(RabbitMQ) 消費者: 連線到代理伺服器

RabbitMQ訊息佇列生產者消費者

概述 生產者生產資料至 RabbitMQ 佇列,消費者消費 RabbitMQ 佇列裡的資料。 詳細 一、準備工作 1、安裝 RabbitMQ 服務和 RabbitMQ Management。 2、在 RabbitMQ 管理介面建立使用者 test

守護程序,互斥鎖,IPC,佇列,生產者消費者模型

小知識點:在子程序中不能使用input輸入! 一.守護程序 守護程序表示一個程序b 守護另一個程序a 當被守護的程序結束後,那麼守護程序b也跟著結束了 應用場景:之所以開子程序,是為了幫助主程序完成某個任務,然而,如果主程序認為自己的事情一旦做完了就沒有必要使用子程序了,就可以將子程序設定為守護程序

【演算法導論】10.1-5陣列實現雙端佇列

演算法導論第三版P131 題目: 10.1-5 棧插入和刪除元素只能在同一端進行,佇列的插入操作和刪除操作分別在兩端進行,與它們不同的,有一種雙端佇列(deque),其插入和刪除操作都可以在兩端進行。寫出4個時間均為O(1)的過程,分別實現在雙端佇列插入和刪除元素的操作,該

Java阻塞佇列實現生產者消費者

生產者 import java.util.Random; import java.util.concurrent.BlockingQueue; //生產者 public class Producer implements Runnable{ private final Block

【RabbitMQ】生產者消費者,通道,佇列,交換器和繫結

  瞭解訊息通訊中的一些重點概念對於深化對RabbitMQ的理解有重要的意義;下面從生產者,消費者,通道,佇列,交換器和繫結,來介紹他們在訊息通訊過程中的角色和作用; 生產者:   建立訊息,然後釋

python 程序鎖 生產者消費者模型 佇列 (守護程序,資料共享等)

#######################總結####### 主要理解 鎖         生產者消費者模型 解耦用的 共享資源的時候 是不安全的 所以用到後面的鎖 守護程序:p.daemon = True  #將該程序設定為守護