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

C++11各種鎖的具體使用

 更新時間:2021年08月10日 11:38:30   作者:1390811049  
本文主要介紹了C++11各種鎖的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

Mutex(互斥鎖)

什么是互斥量(鎖)?

這樣比喻:單位上有一臺打印機(共享數(shù)據(jù)a),你要用打印機(線程1要操作數(shù)據(jù)a),同事老王也要用打印機(線程2也要操作數(shù)據(jù)a),但是打印機同一時間只能給一個人用,此時,規(guī)定不管是誰,在用打印機之前都要向領(lǐng)導(dǎo)申請許可證(lock),用完后再向領(lǐng)導(dǎo)歸還許可證(unlock),許可證總共只有一個,沒有許可證的人就等著在用打印機的同事用完后才能申請許可證(阻塞,線程1lock互斥量后其他線程就無法lock,只能等線程1unlock后,其他線程才能lock)。那么,打印機就是共享數(shù)據(jù),訪問打印機的這段代碼就是臨界區(qū),這個必須互斥使用的許可證就是互斥量(鎖)。

互斥量是為了解決數(shù)據(jù)共享過程中可能存在的訪問沖突的問題。這里的互斥量保證了使用打印機這一過程不被打斷。

死鎖

多線程編程時要考慮多個線程同時訪問共享資源所造成的問題,因此可以通過加鎖解鎖來保證同一時刻只有一個線程能訪問共享資源;使用鎖的時候要注意,不能出現(xiàn)死鎖的狀況;

死鎖就是多個線程爭奪共享資源導(dǎo)致每個線程都不能取得自己所需的全部資源,從而程序無法向下執(zhí)行。

產(chǎn)生死鎖的四個必要條件(面試考點):

互斥(資源同一時刻只能被一個進程使用)請求并保持(進程在請資源時,不釋放自己已經(jīng)占有的資源)不剝奪(進程已經(jīng)獲得的資源,在進程使用完前,不能強制剝奪)循環(huán)等待(進程間形成環(huán)狀的資源循環(huán)等待關(guān)系) 互斥量mutex

互斥量mutex就是互斥鎖,加鎖的資源支持互斥訪問

直接操作 mutex,即直接調(diào)用 mutex 的 lock / unlock 函數(shù)

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>


std::mutex g_mutex;
int g_count = 0;

void Counter() {
  g_mutex.lock();

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;

  // 前面代碼如有異常,unlock 就調(diào)不到了。
  g_mutex.unlock();
}

int main() {
  const std::size_t SIZE = 4;

  // 創(chuàng)建一組線程。
  std::vector<std::thread> v;
  v.reserve(SIZE);

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

  // 等待所有線程結(jié)束。
  for (std::thread& t : v) {
    t.join();
  }

  return 0;
}

lock_guard

使用 lock_guard 自動加鎖、解鎖。原理是 RAII,和智能指針類似。

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex g_mutex;
int g_count = 0;

void Counter() {
  // lock_guard 在構(gòu)造函數(shù)里加鎖,在析構(gòu)函數(shù)里解鎖。
  std::lock_guard<std::mutex> lock(g_mutex);

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;
}

int main() {
  const std::size_t SIZE = 4;

  std::vector<std::thread> v;
  v.reserve(SIZE);

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

  for (std::thread& t : v) {
    t.join();
  }

  return 0;
}

unique_lock

使用 unique_lock 自動加鎖、解鎖。
unique_lock 與 lock_guard 原理相同,但是提供了更多功能(比如可以結(jié)合條件變量使用)。
注意:mutex::scoped_lock 其實就是 unique_lock 的 typedef。

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex g_mutex;
int g_count = 0;

void Counter() {
  std::unique_lock<std::mutex> lock(g_mutex);

  int i = ++g_count;
  std::cout << "count: " << i << std::endl;
}

int main() {
  const std::size_t SIZE = 4;

  std::vector<std::thread> v;
  v.reserve(SIZE);

  for (std::size_t i = 0; i < SIZE; ++i) {
    v.emplace_back(&Counter);
  }

  for (std::thread& t : v) {
    t.join();
  }

  return 0;
}

std::recursive_mutex

就像互斥鎖(mutex)一樣,遞歸互斥鎖(recursive_mutex)是可鎖定的對象,但它允許同一線程獲得對互斥鎖對象的多級所有權(quán)(多次lock)。

這允許從已經(jīng)鎖定它的線程鎖定(或嘗試鎖定)互斥對象,從而獲得對互斥對象的新所有權(quán)級別:互斥對象實際上將保持對該線程的鎖定,直到調(diào)用其成員 unlock 的次數(shù)與此所有權(quán)級別的次數(shù)相同。

#include <iostream>
#include <thread>
#include <mutex> 

std::recursive_mutex mtx;           

void print_block (int n, char c) {
  mtx.lock();
  mtx.lock();
  mtx.lock();
  
  for (int i=0; i<n; ++i) { std::cout << c; }
  std::cout << '\n';
  
  mtx.unlock();
  mtx.unlock();
  mtx.unlock();
}

int main ()
{
  std::thread th1 (print_block,50,'*');
  std::thread th2 (print_block,50,'$');

  th1.join();
  th2.join();

  return 0;
}

std::timed_mutex

定時互斥鎖是一個可時間鎖定的對象,旨在通知何時關(guān)鍵代碼需要獨占訪問,就像常規(guī)互斥鎖一樣,但還支持定時嘗試鎖定請求。

lock 調(diào)用線程將鎖定timed_mutex,并在必要時進行阻塞(其行為與 mutex 完全相同)
try_lock 嘗試鎖定 timed_mutex,而不進行阻塞(其行為與互斥鎖完全相同)
try_lock_for 嘗試鎖定 timed_mutex, 最多阻塞 rel_time 時間
try_lock_until 嘗試鎖定 timed_mutex,最多阻塞到 abs_time 時間點
unlock 解鎖 timed_mutex,釋放對其的所有權(quán)(其行為與 mutex 相同)

std::recursive_timed_mutex

遞歸定時互斥鎖將 recursive_timed 和 timed_mutex 的功能結(jié)合到一個類中:它既支持通過單個線程獲
取多個鎖定級別又支持定時的 try_lock 請求。

成員函數(shù)與 timed_mutex 相同。

once_flag、call_once使用

在多線程中,有一種場景是某個任務(wù)只需要執(zhí)行一次,可以用C++11中的std::call_once函數(shù)配合std::once_flag來實現(xiàn)。多個線程同時調(diào)用某個函數(shù),std::call_once可以保證多個線程對該函數(shù)只調(diào)用一次

實現(xiàn)線程安全的單例模式

//h文件
#pragma once
#include <thread>
#include <iostream>
#include <mutex>
#include <memory>

class Task
{
private:
	Task();
public:
	static Task* task;
	static Task* getInstance();
	void fun();
};
//cpp文件
Task* Task::task;
Task::Task()
{
	std::cout << "構(gòu)造函數(shù)" << std::endl;
}

Task* Task::getInstance()
{
	static std::once_flag flag;
	std::call_once(flag, []
	{
		task = new Task();
	});
	return task;
}

void Task::fun()
{
	std::cout << "hello world!"<< std::endl;
}

條件變量condition_variable:

需要#include<condition_variable>,該頭文件中包含了條件變量相關(guān)的類,其中包括std::condition_variable類

如何使用?std::condition_variable類搭配std::mutex類來使用,std::condition_variable對象(std::condition_variable cond;)的作用不是用來管理互斥量的,它的作用是用來同步線程,它的用法相當于編程中常見的flag標志(A、B兩個人約定flag=true為行動號角,默認flag為false,A不斷的檢查flag的值,只要B將flag修改為true,A就開始行動)。

類比到std::condition_variable,A、B兩個人約定notify_one為行動號角,A就等著(調(diào)用wait(),阻塞),只要B一調(diào)用notify_one,A就開始行動(不再阻塞)。

std::condition_variable的具體使用代碼實例可以參見文章中“生產(chǎn)者與消費者問題”章節(jié)。

wait(locker) :

wait函數(shù)需要傳入一個std::mutex(一般會傳入std::unique_lock對象),即上述的locker。wait函數(shù)會自動調(diào)用 locker.unlock() 釋放鎖(因為需要釋放鎖,所以要傳入mutex)并阻塞當前線程,本線程釋放鎖使得其他的線程得以繼續(xù)競爭鎖。一旦當前線程獲得notify(通常是另外某個線程調(diào)用 notify_* 喚醒了當前線程),wait() 函數(shù)此時再自動調(diào)用 locker.lock()上鎖。

cond.notify_one(): 隨機喚醒一個等待的線程

cond.notify_all(): 喚醒所有等待的線程

condition_variable的wait

std::condition_variable::wait 有兩個重載:

void wait( std::unique_lock<std::mutex>& lock );                    (1)	(since C++11)

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );    (2)	(since C++11)

void wait( std::unique_lockstd::mutex& lock )

先unlock之前獲得的mutex,然后阻塞當前的執(zhí)行線程。把當前線程添加到等待線程列表中,該線程會持續(xù) block 直到被 notify_all() 或 notify_one() 喚醒。被喚醒后,該thread會重新獲取mutex,獲取到mutex后執(zhí)行后面的動作。

線程block時候也可能被意外或者錯誤喚醒。

template< class Predicate > void wait( std::unique_lockstd::mutex& lock, Predicate pred );

該重載設(shè)置了第二個參數(shù) Predicate, 只有當pred為false時,wait才會阻塞當前線程。蓋崇仔等同于下面:

該情況下,線程被喚醒后,先重新判斷pred的值。如果pred為false,則會釋放mutex并重新阻塞在wait。因此,該mutex必須有pred的權(quán)限。該重載消除了意外喚醒的影響。

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <chrono>

std::deque<int> q;
std::mutex mu;
std::condition_variable condi;

void function_1()
{
	int count = 10;
	while (count > 0)
	{
		std::unique_lock<std::mutex> locker(mu);
		q.push_back(count);
		locker.unlock();
		condi.notify_one();			//通知一個等待線程激活   condi.notify_all()激活所有線程
		count--;
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
}

void function_2()
{
	int data = 100;
	while (data > 1)
	{
		std::unique_lock<std::mutex> locker(mu);
		condi.wait(locker,			//解鎖locker,并進入休眠  收到notify時又重新加鎖
			[]() { return !q.empty(); });   //如果q不為空 線程才會被激活

		data = q.front();
		q.pop_front();
		locker.unlock();

		std::cout << data << std::endl;
	}
}
int main()
{
	std::thread t1(function_1);
	std::thread t2(function_2);

	t1.join();
	t2.join();
	
	return 0;
}

std::shared_mutex

std::shared_mutex 是讀寫鎖,提供兩種訪問權(quán)限的控制:共享性(shared)和排他性(exclusive)。通過lock/try_lock獲取排他性訪問權(quán)限,通過lock_shared/try_lock_shared獲取共享性訪問權(quán)限。這樣的設(shè)置對于區(qū)分不同線程的讀寫操作特別有用。shared_mutex是c++17中引入的,使用時需要注意編譯器版本。

#include <iostream>
#include <mutex>  // For std::unique_lock
#include <shared_mutex>
#include <thread>

class ThreadSafeCounter {
 public:
  ThreadSafeCounter() = default;

  // Multiple threads/readers can read the counter's value at the same time.
  unsigned int get() const {
    std::shared_lock lock(mutex_);
    return value_;
  }

  // Only one thread/writer can increment/write the counter's value.
  void increment() {
    std::unique_lock lock(mutex_);
    value_++;
  }

  // Only one thread/writer can reset/write the counter's value.
  void reset() {
    std::unique_lock lock(mutex_);
    value_ = 0;
  }

 private:
  mutable std::shared_mutex mutex_;
  unsigned int value_ = 0;
};

int main() {
  ThreadSafeCounter counter;

  auto increment_and_print = [&counter]() {
    for (int i = 0; i < 3; i++) {
      counter.increment();
      std::cout << std::this_thread::get_id() << ' ' << counter.get() << '\n';

      // Note: Writing to std::cout actually needs to be synchronized as well
      // by another std::mutex. This has been omitted to keep the example small.
    }
  };

  std::thread thread1(increment_and_print);
  std::thread thread2(increment_and_print);

  thread1.join();
  thread2.join();
}

原子操作

所謂原子操作,就是多線程程序中“最小的且不可并行化的”操作。對于在多個線程間共享的一個資源而言,這意味著同一時刻,多個線程中有且僅有一個線程在對這個資源進行操作,即互斥訪問。提到“互斥”訪問,熟悉多線程開發(fā)的同學(xué)可能立即想到Windows平臺下使用的臨界區(qū)/CRITICAL_SECTION、互斥體/Mutex。實現(xiàn)互斥通常需要平臺相關(guān)的特殊指令,在C++11標準之前,這意味著需要在C/C++代碼中嵌入平臺相關(guān)的內(nèi)聯(lián)匯編代碼。 平臺相關(guān)意味著:1.你必須了解平臺相關(guān)的編譯器擴展;2.無法跨平臺運行你的多線程程序。

多線程中需要同步的總是資源/數(shù)據(jù),而不是代碼。因此C++11對數(shù)據(jù)進行了更為良好的抽象,引入"原子數(shù)據(jù)類型"/atomic類型,以達到對開發(fā)者掩蓋互斥鎖、臨界區(qū)的目的。要知道,這些臨界區(qū)、互斥鎖才是平臺相關(guān)的東西。來看下面的示例代碼。

#include<atomic>
#include<thread>
#include<iostream>

using namespace std;
std::atomic_llong total{ 0 };//原子數(shù)據(jù)類型

void func(int)
{
    for (long long i = 0; i<100000000LL; ++i)
    {
        total += i;
    }
}

int main()
{
    thread t1(func, 0);
    thread t2(func, 0);
    t1.join();
    t2.join();
    cout<<total<<endl;//9999999900000000
    return 0;
}

原子數(shù)據(jù)類型/atomic類型

atomic模板類

template <class T> struct atomic

//example
#include<atomic>

void test()
{
    std::atomic_int nThreadData; // std::atomic_int  <----> std::atomic<int>
    nThreadData = 10;
    nThreadData.store(10);
    //TODO: use nThreadData here;
}

對于內(nèi)置型數(shù)據(jù)類型,C11和C++11標準中都已經(jīng)提供了實例化原子類型,如下表所示:

在這里插入圖片描述

atomic類型原子操作接口如下

在這里插入圖片描述

到此這篇關(guān)于C++11各種鎖的具體使用的文章就介紹到這了,更多相關(guān)C++11各種鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談Qt中使用CEF的幾個要點(Windows下)

    淺談Qt中使用CEF的幾個要點(Windows下)

    下面小編就為大家?guī)硪黄獪\談Qt中使用CEF的幾個要點(Windows下)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 詳解如何將Spire.Doc for C++集成到C++程序中

    詳解如何將Spire.Doc for C++集成到C++程序中

    Spire.Doc for C++是一個專業(yè)的Word庫,供開發(fā)人員在任何類型的C++應(yīng)用程序中閱讀、創(chuàng)建、編輯、比較和轉(zhuǎn)換 Word 文檔,本文演示了如何以兩種不同的方式將 Spire.Doc for C++ 集成到您的 C++ 應(yīng)用程序中,希望對大家有所幫助
    2023-05-05
  • C語言實現(xiàn)猜數(shù)字

    C語言實現(xiàn)猜數(shù)字

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-01-01
  • C++ map用法總結(jié)(整理)

    C++ map用法總結(jié)(整理)

    這篇文章主要介紹了C++ map用法總結(jié)(整理),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • C語言模擬實現(xiàn)字符串庫函數(shù)的示例講解

    C語言模擬實現(xiàn)字符串庫函數(shù)的示例講解

    這篇文章主要為大家詳細介紹了C語言模擬實現(xiàn)字符串庫函數(shù)的具體方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01
  • c語言中聯(lián)合體和枚舉用法詳解

    c語言中聯(lián)合體和枚舉用法詳解

    結(jié)構(gòu)體、聯(lián)合體是C語言中的構(gòu)造類型,結(jié)構(gòu)體我們平時應(yīng)該都用得很多,下面這篇文章主要給大家介紹了關(guān)于c語言中聯(lián)合體和枚舉用法的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-12-12
  • C語言判斷語句和if語句形式及多層if使用示例

    C語言判斷語句和if語句形式及多層if使用示例

    這篇文章主要為大家介紹了C語言判斷語句和if語句形式及多層if使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • C語言判斷字符串是否回文三種方法實例

    C語言判斷字符串是否回文三種方法實例

    回文就是字符串中心對稱,從左向右讀和從右向左讀的內(nèi)容是一樣的,下面這篇文章主要給大家介紹了關(guān)于C語言判斷字符串是否回文的三種方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-03-03
  • vscode刷acm、leetcode的題目

    vscode刷acm、leetcode的題目

    vscode是一款越來越受碼農(nóng)們喜愛的軟件,大多數(shù)人學(xué)習(xí)編程繞不開的一部分就是算法,很多人都喜歡刷LeetCode的題目,本文就來介紹一下
    2021-06-06
  • C語言中的結(jié)構(gòu)體的入門學(xué)習(xí)教程

    C語言中的結(jié)構(gòu)體的入門學(xué)習(xí)教程

    這篇文章主要介紹了C語言中的結(jié)構(gòu)體的入門學(xué)習(xí)教程,以struct語句定義的結(jié)構(gòu)體是C語言編程中的重要基礎(chǔ),需要的朋友可以參考下
    2015-12-12

最新評論