欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你搞懂C++中的流量控制

 更新時間:2023年10月26日 14:42:05   作者:文成宇  
限流可以認為服務(wù)降級的一種,限流就是限制系統(tǒng)的輸入和輸出流量已達到保護系統(tǒng)的目的,這篇文章小編就來帶大家深入了解一下如何利用C++實現(xiàn)流量控制吧

流量控制

一般并發(fā)系統(tǒng)有對應(yīng)處理請求的最大能力,這里稱最大qps,也需要有閾值設(shè)置,如果超過最大qps,則可能導(dǎo)致系統(tǒng)不穩(wěn)定,產(chǎn)生雪崩效應(yīng),甚至連鎖反應(yīng)。

限流可以認為服務(wù)降級的一種,限流就是限制系統(tǒng)的輸入和輸出流量已達到保護系統(tǒng)的目的。一般來說系統(tǒng)的吞吐量是可以被測算的,為了保證系統(tǒng)的穩(wěn)定運行,一旦達到的需要限制的閾值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:部分拒絕處理。

這也是最常見的場景,流控是為了保護下游有限的資源不被流量沖垮,保證服務(wù)的可用性,一般允許流控的閾值有一定的彈性,偶爾的超量訪問是可以接受的。

有的場景下,流控服務(wù)于收費模式,比如某些云廠商會對調(diào)用 API 的頻次進行計費。既然涉及到錢,一般不允許有超出閾值的調(diào)用量。

1 固定窗口

流控是為了限制指定時間間隔內(nèi)能夠允許的訪問量,因此,最直觀的思路就是基于一個給定的時間窗口,維護一個計數(shù)器用于統(tǒng)計訪問次數(shù),然后實現(xiàn)以下規(guī)則:

  • 如果訪問次數(shù)小于閾值,則代表允許訪問,訪問次數(shù) +1。
  • 如果訪問次數(shù)超出閾值,則限制訪問,訪問次數(shù)不增。
  • 如果超過了時間窗口,計數(shù)器清零,并重置清零后的首次成功訪問時間為當(dāng)前時間。這樣就確保計數(shù)器統(tǒng)計的是最近一個窗口的訪問量。

代碼實現(xiàn)

#include <chrono>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <thread>

const int NS_PER_SECOND = 1e9;

// 獲取當(dāng)前時間戳, 單位納秒
int64_t Now() {
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return ts.tv_sec * NS_PER_SECOND + ts.tv_nsec;
}

class RateLimiter {
  // 時間窗口, 單位秒
  int64_t window_;
  // 時間窗口內(nèi)最大允許的閾值
  int64_t threshold_;
  // 當(dāng)前時間窗口的起始時間
  int64_t start_time_ = Now();
  // 計數(shù)器
  int64_t counter_;

 public:
  RateLimiter(int64_t window, int64_t threshold)
      : window_(window), threshold_(threshold) {}
  /**
   * @param permits 配額數(shù)量
   * @return 申請成功則返回true,否則返回false
   */
  bool tryAcquire(int permits) {
    long now = Now();
    if (now - start_time_ > window_ * NS_PER_SECOND) {
      counter_ = 0;
      start_time_ = now;
    }
    if (counter_ + permits <= threshold_) {
      counter_ += permits;
      return true;
    } else {
      return false;
    }
  }
};

int main() {
  // 限流300MB/s
  RateLimiter limiter(1, 300 * 1024 * 1024);
  // 每次請求256KB
  int64_t request_size = 256 * 1024;
  // 每隔約1ms發(fā)起2次請求
  // 每隔1s打印一次請求數(shù)據(jù)量
  int64_t start_time = Now();
  int64_t last_time = start_time;
  int64_t last_size = 0;
  while (true) {
    int64_t now = Now();
    if (now - last_time >= NS_PER_SECOND) {
      printf("time: %lf s, request size: %lf MB\n", (now - last_time) / 1e9,
             (last_size / 1024.0 / 1024.0));
      last_time = now;
      last_size = 0;
    }
    for (int i = 0; i < 2; ++i) {
      if (limiter.tryAcquire(request_size)) {
        last_size += request_size;
      }
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }
  return 0;
}

注意: 示例為單線程,多線程需要考慮線程安全問題。

臨界突變問題

固定窗口的流控實現(xiàn)非常簡單,以 1 分鐘允許 100 次訪問為例,如果流量均勻保持 200 次/分鐘的訪問速率,系統(tǒng)的訪問量曲線大概是這樣的(按分鐘清零):

但如果流量并不均勻,假設(shè)在時間窗口開始時刻 0:00 有幾次零星的訪問,一直到 0:50 時刻,開始以 10 次/秒的速度請求,就會出現(xiàn)這樣的訪問量圖線:

在臨界的 20 秒內(nèi)(0:50~1:10)系統(tǒng)承受的實際訪問量是 200 次,換句話說,最壞的情況下,在窗口臨界點附近系統(tǒng)會承受 2 倍的流量沖擊,這就是固定窗口不能解決的臨界突變問題。

2 滑動窗口

如何解決固定窗口算法的臨界突變問題?既然一個窗口統(tǒng)計的精度低,那么可以把整個大的時間窗口切分成更細粒度的子窗口,每個子窗口獨立統(tǒng)計。同時,每過一個子窗口大小的時間,就向右滑動一個子窗口。這就是滑動窗口算法的思路。

如上圖所示,將一分鐘的時間窗口切分成 6 個子窗口,每個子窗口維護一個獨立的計數(shù)器用于統(tǒng)計 10 秒內(nèi)的訪問量,每經(jīng)過 10s,時間窗口向右滑動一格。

回到固定窗口出現(xiàn)臨界跳變的例子,結(jié)合上面的圖再看滑動窗口如何消除臨界突變。如果 0:50 到 1:00 時刻(對應(yīng)灰色的格子)進來了 100 次請求,接下來 1:00~1:10 的 100 次請求會落到黃色的格子中,由于算法統(tǒng)計的是 6 個子窗口的訪問量總和,這時候總和超過設(shè)定的閾值 100,就會拒絕后面的這 100 次請求。

(代碼實現(xiàn)請參考 Sentinel)

精度問題

現(xiàn)在思考這么一個問題:滑動窗口算法能否精準(zhǔn)地控制任意給定時間窗口 T 內(nèi)的訪問量不大于 N?

答案是否定的,還是將 1 分鐘分成 6 個 10 秒大小的子窗口的例子,假設(shè)請求的速率現(xiàn)在是 20 次/秒,從 0:05 時刻開始進入,那么在 0:050:10 時間段內(nèi)會放進 100 個請求,同時接下來的請求都會被限流,直到 1:00 時刻窗口滑動,在 1:001:05 時刻繼續(xù)放進 100 個請求。如果把 0:05~1:05 看作是 1 分鐘的時間窗口,那么這個窗口內(nèi)實際的請求量是 200,超出了給定的閾值 100。

如果要追求更高的精度,理論上只需要把滑動窗口切分得更細。像 Sentinel 中就可以通過修改單位時間內(nèi)的采樣數(shù)量 sampleCount 值來設(shè)置精度,這個值一般根據(jù)業(yè)務(wù)的需求來定,以達到在精度和內(nèi)存消耗之間的平衡。

平滑度問題

使用滑動窗口算法限制流量時,我們經(jīng)常會看到像下面一樣的流量曲線。

突發(fā)的大流量在窗口開始不久就直接把限流的閾值打滿,導(dǎo)致剩余的窗口內(nèi)所有請求都無法通過。在時間窗口的單位比較大時(例如以分為單位進行流控),這種問題的影響就比較大了。在實際應(yīng)用中我們要的限流效果往往不是把流量一下子掐斷,而是讓流量平滑地進入系統(tǒng)當(dāng)中。

3 漏桶

滑動窗口無法很好地解決平滑度問題,再回過頭看我們對于平滑度的訴求,當(dāng)流量超過一定范圍后,我們想要的效果不是一下子切斷流量,而是將流量控制在系統(tǒng)能承受的一定的速度內(nèi)。假設(shè)平均訪問速率為 v, 那我們要做的流控其實是流速控制,即控制平均訪問速率 v ≤ N / T。

在網(wǎng)絡(luò)通信中常常用到漏桶算法來實現(xiàn)流量整形。漏桶算法的思路就是基于流速來做控制。想象一下上學(xué)時經(jīng)常做的水池一邊抽水一邊注水的應(yīng)用題,把水池換成水桶(還是底下有洞一注水就開始漏的那種),把請求看作是往桶里注水,桶底漏出的水代表離開緩沖區(qū)被服務(wù)器處理的請求,桶口溢出的水代表被丟棄的請求。在概念上類比:

  • 最大允許請求數(shù) N :桶的大小
  • 時間窗口大小 T :一整桶水漏完的時間
  • 最大訪問速率 V :一整桶水漏完的速度,即 N/T
  • 請求被限流 :桶注水的速度比漏水的速度快,最終桶滿了, 裝不了水

假設(shè)起始時刻桶是空的,每次訪問都會往桶里注入一單位體積的水量,那么當(dāng)我們以小于等于 N/T 的速度往桶里注水時,桶內(nèi)的水就永遠不會溢出。反之,一旦實際注水速度超過漏水速度,桶里就會積累越來越多的水,直到桶滿。同時漏水的速度永遠被控制在 N/T 以內(nèi),這就實現(xiàn)了平滑流量的目的。

漏桶算法的訪問速率曲線如下:

附上一張網(wǎng)上常見的漏桶算法原題圖:

代碼實現(xiàn) LeakyBucket

class LeakyBucket {
  int64_t last_time_ = Now();
  int64_t rate_;
  int64_t debt_;
  // 精度
  int64_t precision_;
  int64_t quota_;

 public:
  LeakyBucket(int64_t rate, double precision = 1000)
      : rate_(rate), precision_(precision) {
    quota_ = rate_ / precision_;
  }

  bool tryAcquire(int permits) {
    int64_t now = Now();
    int64_t pay_off = (now - last_time_) * rate_ / NS_PER_SECOND;
    int64_t left_debt = std::max(0LL, debt_ - pay_off);
    if (left_debt > quota_) {
      return false;
    }
    debt_ = left_debt + permits;
    last_time_ = now;
    return true;
  }
};
class LeakyBucket {
  int64_t last_time_ = Now();
  int64_t rate_;
  int64_t debt_;
  // 精度
  int64_t precision_;
  int64_t quota_;

 public:
  LeakyBucket(int64_t rate, double precision = 1000)
      : rate_(rate), precision_(precision) {
    quota_ = rate_ / precision_;
  }

  bool tryAcquire(int permits) {
    int64_t now = Now();
    int count = (now - last_time_) / (NS_PER_SECOND / precision_);
    int64_t pay_off = count * quota_;
    int64_t left_debt = std::max(0LL, debt_ - pay_off);
    if (left_debt > quota_) {
      return false;
    }
    debt_ = left_debt + permits;
    last_time_ += count * (NS_PER_SECOND / precision_);
    return true;
  }
};

漏桶的問題

漏桶的優(yōu)勢在于能夠平滑流量,如果流量不是均勻的,那么漏桶算法與滑動窗口算法一樣無法做到真正的精確控制。

雖然可以通過限制桶大小的方式使得訪問量控制在 N 以內(nèi),但這樣做的副作用是流量在還未達到限制條件就被禁止。

還有一個隱含的約束是,漏桶漏水的速度最好是一個整數(shù)值(即容量 N 能夠整除時間窗口大小 T ),否則在計算剩余水量時會有些許誤差。

4 令牌桶

漏桶模型中,請求來了是往桶里注水; 令牌桶的思想則完全相反,把請求放行變成從桶里抽水,對應(yīng)的,把注水看作是補充系統(tǒng)可承受流量。

令牌桶算法的原理是系統(tǒng)以恒定的速率往桶里放入令牌,令牌桶有一個容量,當(dāng)令牌桶滿了,再向桶里放的令牌會被丟棄;當(dāng)一個請求需要被處理,需要從令牌桶中取出一個令牌,如果此時令牌桶中沒有令牌可取,那么拒絕該請求。

代碼實現(xiàn)

class TokenBucket {
 public:
  TokenBucket(int64_t capacity, int64_t rate)
      : capacity_(capacity),
        rate_(rate),
        supply_unit_time_(NS_PER_SECOND / rate) {}

  // 嘗試獲取令牌
  bool tryAcquire(int permits) {
    int64_t cur = Now();
    int64_t new_tokens = (cur - last_time_) / supply_unit_time_;
    // 更新補充時間, 不能直接=cur, 否則會導(dǎo)致時間丟失
    last_time_ += new_tokens * supply_unit_time_;
    tokens_ = std::min(capacity_, tokens_ + new_tokens);
    if (tokens_ >= permits) {
      tokens_ -= permits;
      return true;
    } else {
      return false;
    }
  }

 private:
  // 當(dāng)前桶內(nèi)的令牌數(shù)
  int64_t tokens_ = 0;
  // 上次補充令牌的時間(單位納秒)
  int64_t last_time_ = Now();
  // 令牌桶大小
  int64_t capacity_;
  // 補充令牌的速率
  int64_t rate_;
  // 補充令牌的單位時間
  const int64_t supply_unit_time_;
};
class TokenBucket {
 public:
  TokenBucket(int64_t capacity, int64_t rate)
      : capacity_(capacity),
        rate_(rate),
        supply_unit_time_(NS_PER_SECOND / rate) {}

  // 嘗試獲取令牌
  bool tryAcquire(int permits) {
    int64_t now = Now();
    int64_t count = (now - last_time_) / supply_unit_time_;
    int64_t new_tokens = count * supply_unit_time_ * rate_ / NS_PER_SECOND;
    // 更新補充時間, 不能直接=now, 否則會導(dǎo)致時間丟失
    last_time_ += count * supply_unit_time_;
    tokens_ = std::min(capacity_, tokens_ + new_tokens);
    if (tokens_ >= permits) {
      tokens_ -= permits;
      return true;
    } else {
      return false;
    }
  }

 private:
  // 當(dāng)前桶內(nèi)的令牌數(shù)
  int64_t tokens_ = 0;
  // 上次補充令牌的時間(單位納秒)
  int64_t last_time_ = Now();
  // 令牌桶大小
  int64_t capacity_;
  // 補充令牌的速率
  int64_t rate_;
  // 補充令牌的單位時間
  const int64_t supply_unit_time_ = NS_PER_US;
};
class DynamicTokenBucket {
  double zeroTime_;

 public:
  explicit DynamicTokenBucket(double zeroTime = 0) noexcept
      : zeroTime_(zeroTime) {}

  bool tryAcquire(double permits, double rate, double burstSize,
                  int64_t now = Now()) {
    double tokens = std::min((now / 1e9 - zeroTime_) * rate, burstSize);
    if (tokens < permits) {
      return false;
    }
    tokens -= permits;
    zeroTime_ = now / 1e9 - tokens / rate;

    return true;
  }
};

class TokenBucket {
  DynamicTokenBucket tokenBucket_;
  double rate_;
  double burstSize_;

 public:
  TokenBucket(double rate, double burstSize, double zeroTime = 0) noexcept
      : tokenBucket_(zeroTime), rate_(rate), burstSize_(burstSize) {}

  bool tryAcquire(double permits, int64_t now = Now()) {
    return tokenBucket_.tryAcquire(permits, rate_, burstSize_, now);
  }
};

漏桶、令牌桶的區(qū)別

雖然兩者本質(zhì)上只是反轉(zhuǎn)了一下,不過在實際使用中,適用的場景稍有差別:

1)漏桶:用于控制網(wǎng)絡(luò)中的速率。在該算法中,輸入速率可以變化,但輸出速率保持恒定。常常配合一個 FIFO 隊列使用。

2)令牌桶:按照固定速率往桶中添加令牌,允許輸出速率根據(jù)突發(fā)大小而變化。

舉個例子,一個系統(tǒng)限制 60 秒內(nèi)的最大訪問量是 60 次,換算速率是 1 次/秒,如果在一段時間內(nèi)沒有訪問量,那么對漏桶而言此刻是空的?,F(xiàn)在,一瞬間涌入 60 個請求,那么流量整形后,漏桶會以每秒 1 個請求的速度,花上 1 分鐘將 60 個請求漏給下游。換成令牌桶的話,則是從令牌桶中一次性取走 60 個令牌,一下子塞給下游。

5 滑動日志

一般情況下,上述的算法已經(jīng)能很好地用于大部分實際應(yīng)用場景了,很少有場景需要真正完全精確的控制(即任意給定時間窗口T內(nèi)請求量不大于 N )。如果要精確控制的話,我們需要記錄每一次用戶請求日志,當(dāng)每次流控判斷時,取出最近時間窗口內(nèi)的日志數(shù),看是否大于流控閾值。這就是滑動日志的算法思路。

設(shè)想某一個時刻 t 有一個請求,要判斷是否允許,我們要看的其實是過去 t - N 時間段內(nèi)是否有大于等于 N 個請求被放行,因此只要系統(tǒng)維護一個隊列 q,里面記錄每一個請求的時間,理論上就可以計算出從 t - N 時刻開始的請求數(shù)。

考慮到只需關(guān)心當(dāng)前時間之前最長 T 時間內(nèi)的記錄,因此隊列 q 的長度可以動態(tài)變化,并且隊列中最多只記錄 N 條訪問,因此隊列長度的最大值為 N。

滑動日志與滑動窗口非常像,區(qū)別在于滑動日志的滑動是根據(jù)日志記錄的時間做動態(tài)滑動,而滑動窗口是根據(jù)子窗口的大小,以子窗口維度滑動。

偽代碼實現(xiàn)

算法的偽代碼表示如下:

# 初始化
counter = 0
q = []

# 請求處理流程
# 1.找到隊列中第一個時間戳>=t-T的請求,即以當(dāng)前時間t截止的時間窗口T內(nèi)的最早請求
t = now
start = findWindowStart(q, t)

# 2.截斷隊列,只保留最近T時間窗口內(nèi)的記錄和計數(shù)值
q = q[start, q.length - 1]
counter -= start

# 3.判斷是否放行,如果允許放行則將這次請求加到隊列 q 的末尾
if counter < threshold
    push(q, t)
    counter++
    # 放行
else
    # 限流

findWindowStart 的實現(xiàn)依賴于隊列 q 使用的數(shù)據(jù)結(jié)構(gòu),以簡單的數(shù)組為例,可以使用二分查找等方式。后面也會看到使用其他數(shù)據(jù)結(jié)構(gòu)如何實現(xiàn)。

如果用數(shù)組實現(xiàn),一個難點可能是如何截斷一個隊列,一種可行的思路是使用一組頭尾指針 head 和 tail 分別指向數(shù)組中最近和最早的有效記錄索引來解決, findWindowStart 的實現(xiàn)就變成在 tail 和 head 之間查找對應(yīng)元素。

復(fù)雜度問題

雖然算法解決了精確度問題,但代價也是顯而易見的。

首先,我們要保存一個長度最大為 N 的隊列,這意味著空間復(fù)雜度達到 O(N),如果要針對不同的 key 做流控,那么空間上會占用更多。當(dāng)然,可以對不活躍 key 的隊列進行復(fù)用來降低內(nèi)存消耗。

其次,我們需要在隊列中確定時間窗口,即通過 findWindowStart 方法尋找不早于當(dāng)前時間戳 t - N 的請求記錄。以二分查找為例,時間復(fù)雜度是 O(logN)。

總結(jié)

這里按我的個人理解總結(jié)了上述幾種流控算法的復(fù)雜度和適用場景。

算法時間復(fù)雜度空間復(fù)雜度適用場景
固定窗口O(1)O(1)容易實現(xiàn),適用于一些簡單的流控場景,流量比較均勻,或者允許臨界突變
滑動窗口O(1)O(M) - M為子窗口數(shù)適用大多數(shù)場景,可以通過調(diào)節(jié)采樣子窗口數(shù)來平衡開銷
漏桶算法O(1)O(1)要求輸出速率恒定的場景,能夠平滑流量
令牌桶算法O(1)O(1)與漏桶類似,區(qū)別在于允許一定的突發(fā)流量
滑動日志O(log(N)) 取決于選擇的數(shù)據(jù)結(jié)構(gòu)O(N) - N為時間窗口內(nèi)允許的最大請求量要求完全精確的控制,保證任意T時刻內(nèi)流量不超過N,高時間和高空間復(fù)雜度,性能最差

完整代碼

#include <chrono>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <thread>
const int NS_PER_SECOND = 1e9;
const int NS_PER_MS = 1e6;
const int NS_PER_US = 1e3;
// 獲取當(dāng)前時間戳, 單位納秒
int64_t Now() {
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return ts.tv_sec * NS_PER_SECOND + ts.tv_nsec;
}
class RateLimiter {
  // 時間窗口, 單位秒
  int64_t window_;
  // 時間窗口內(nèi)最大允許的閾值
  int64_t threshold_;
  // 當(dāng)前時間窗口的起始時間
  int64_t start_time_ = Now();
  // 計數(shù)器
  int64_t counter_;
 public:
  RateLimiter(int64_t window, int64_t threshold)
      : window_(window), threshold_(threshold) {}
  bool tryAcquire(int permits) {
    long now = Now();
    if (now - start_time_ > window_ * NS_PER_SECOND) {
      counter_ = 0;
      start_time_ = now;
    }
    if (counter_ + permits <= threshold_) {
      counter_ += permits;
      return true;
    } else {
      return false;
    }
  }
};
class LeakyBucket {
  int64_t last_time_ = Now();
  int64_t rate_;
  int64_t debt_;
  // 精度
  int64_t precision_;
  int64_t quota_;
 public:
  LeakyBucket(int64_t rate, double precision = 1000)
      : rate_(rate), precision_(precision) {
    quota_ = rate_ / precision_;
  }
  bool tryAcquire(int permits) {
    int64_t now = Now();
    int count = (now - last_time_) / (NS_PER_SECOND / precision_);
    int64_t pay_off = count * quota_;
    int64_t left_debt = std::max(0LL, debt_ - pay_off);
    if (left_debt > quota_) {
      return false;
    }
    debt_ = left_debt + permits;
    last_time_ += count * (NS_PER_SECOND / precision_);
    return true;
  }
};
class TokenBucket {
 public:
  TokenBucket(int64_t capacity, int64_t rate)
      : capacity_(capacity),
        rate_(rate),
        supply_unit_time_(NS_PER_SECOND / rate) {}
  // 嘗試獲取令牌
  bool tryAcquire(int permits) {
    int64_t now = Now();
    int64_t count = (now - last_time_) / supply_unit_time_;
    int64_t new_tokens = count * supply_unit_time_ * rate_ / NS_PER_SECOND;
    // 更新補充時間, 不能直接=now, 否則會導(dǎo)致時間丟失
    last_time_ += count * supply_unit_time_;
    tokens_ = std::min(capacity_, tokens_ + new_tokens);
    if (tokens_ >= permits) {
      tokens_ -= permits;
      return true;
    } else {
      return false;
    }
  }
 private:
  // 當(dāng)前桶內(nèi)的令牌數(shù)
  int64_t tokens_ = 0;
  // 上次補充令牌的時間(單位納秒)
  int64_t last_time_ = Now();
  // 令牌桶大小
  int64_t capacity_;
  // 補充令牌的速率
  int64_t rate_;
  // 補充令牌的單位時間
  const int64_t supply_unit_time_ = NS_PER_US;
};
int main() {
  // 限流300MB/s
  // RateLimiter limiter(1, 300 * 1024 * 1024);
  // LeakyBucket limiter(300 * 1024 * 1024);
  TokenBucket limiter(1024 * 1024, 300 * 1024 * 1024);
  // 每次請求256KB
  int64_t request_size = 256 * 1024;
  // 每隔約1ms發(fā)起2次請求
  // 每隔1s打印一次請求數(shù)據(jù)量
  int64_t start_time = Now();
  int64_t last_time = start_time;
  int64_t last_size = 0;
  while (true) {
    int64_t now = Now();
    if (now - last_time >= NS_PER_SECOND) {
      printf("time: %lf s, request size: %lf MB\n", (now - last_time) / 1e9,
             (last_size / 1024.0 / 1024.0));
      last_time = now;
      last_size = 0;
    }
    for (int i = 0; i < 2; ++i) {
      if (limiter.tryAcquire(request_size)) {
        last_size += request_size;
      }
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }
  return 0;
}

到此這篇關(guān)于一文帶你搞懂C++中的流量控制的文章就介紹到這了,更多相關(guān)C++流量控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++程序中啟動線程的方法

    C++程序中啟動線程的方法

    這篇文章主要介紹了C++程序中啟動線程的方法,作者針對C++11版本中的一些新特性進行了解說,需要的朋友可以參考下
    2015-07-07
  • C++ template用法案例詳解

    C++ template用法案例詳解

    這篇文章主要介紹了C++ template用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • 通過C語言判斷字符串是否為點分十進制的IP地址

    通過C語言判斷字符串是否為點分十進制的IP地址

    這篇文章主要為大家詳細介紹了如何通過C語言判斷字符串是否為點分十進制的IP地址,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2023-03-03
  • ubuntu系統(tǒng)下C++調(diào)用matlab程序的方法詳解

    ubuntu系統(tǒng)下C++調(diào)用matlab程序的方法詳解

    學(xué)習(xí)c++與matlab混合編程一般是通過c++調(diào)用matlab函數(shù),因為matlab具有強大的數(shù)學(xué)函數(shù)庫,然而vc++具有界面設(shè)計靈活的優(yōu)點,下面這篇文章主要給大家介紹了關(guān)于在ubuntu系統(tǒng)下C++調(diào)用matlab程序的方法,需要的朋友可以參考下。
    2017-08-08
  • C語言數(shù)據(jù)結(jié)構(gòu)之雙向循環(huán)鏈表的實例

    C語言數(shù)據(jù)結(jié)構(gòu)之雙向循環(huán)鏈表的實例

    這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之雙向循環(huán)鏈表的實例的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C語言實現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    C語言實現(xiàn)BMP圖像處理(彩色圖轉(zhuǎn)灰度圖)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)BMP圖像處理,彩色圖轉(zhuǎn)灰度圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • 淺析C++11新特性的Lambda表達式

    淺析C++11新特性的Lambda表達式

    C++11 新增了很多特性,lambda 表達式是其中之一,本文涉及到C++11這次更新中較為重要的lambda表達式。有需要的朋友們可以參考學(xué)習(xí)。
    2016-08-08
  • C語言Static?關(guān)鍵字解析

    C語言Static?關(guān)鍵字解析

    這篇文章主要介紹了C語言Static?關(guān)鍵字解析,C語言中staic關(guān)鍵字很簡單,簡單到你的任何一個項目中可以不寫一個staic關(guān)鍵字也是沒有問題的。寫這篇章主要是一下自己的staic的理解和應(yīng)用,當(dāng)然在章開頭依舊要照本宣科簡述一下static關(guān)鍵字,需要的朋友可以參考一下
    2022-02-02
  • C++ Boost Serialization庫超詳細獎金額

    C++ Boost Serialization庫超詳細獎金額

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴展的一些C++程序庫的總稱
    2022-12-12
  • C語言的getc()函數(shù)和gets()函數(shù)的使用對比

    C語言的getc()函數(shù)和gets()函數(shù)的使用對比

    這篇文章主要介紹了C語言的getc()函數(shù)和gets()函數(shù)的使用對比,從數(shù)據(jù)流中一個是讀取字符一個是讀取字符串,需要的朋友可以參考下
    2015-08-08

最新評論