中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++11中thread多線程編程如何創建

發布時間:2021-12-06 12:20:56 來源:億速云 閱讀:166 作者:小新 欄目:開發技術

這篇文章主要為大家展示了“C++11中thread多線程編程如何創建”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“C++11中thread多線程編程如何創建”這篇文章吧。

    1 線程創建與結束

    C++11 新標準中引入了四個頭文件來支持多線程編程,他們分別是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。

    • <atomic>:該頭文主要聲明了兩個類, std::atomic 和 std::atomic_flag,另外還聲明了一套 C 風格的原子類型和與 C 兼容的原子操作的函數。

    • <thread>:該頭文件主要聲明了 std::thread 類,另外 std::this_thread 命名空間也在該頭文件中。

    • <mutex>:該頭文件主要聲明了與互斥量(mutex)相關的類,包括 std::mutex 系列類,std::lock_guard, std::unique_lock, 以及其他的類型和函數。<condition_variable>:該頭文件主要聲明了與條件變量相關的類,包括 std::condition_variable 和 std::condition_variable_any。

    • <future>:該頭文件主要聲明了 std::promise, std::package_task 兩個 Provider 類,以及 std::future 和 std::shared_future 兩個 Future 類,另外還有一些與之相關的類型和函數,std::async() 函數就聲明在此頭文件中。

    #include <iostream>
    #include <utility>
    #include <thread>
    #include <chrono>
    #include <functional>
    #include <atomic>
     
    void f1(int n)
    {
      for (int i = 0; i < 5; ++i) {
        std::cout << "Thread " << n << " executing\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }
    }
     
    void f2(int& n)
    {
      std::cout << "thread-id:" << std::this_thread::get_id() << "\n";
      for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing:" << n << "\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }
    }
     
    int main()
    {
      int n = 0;
      std::thread t1; // t1 is not a thread t1 不是一個線程
      std::thread t2(f1, n + 1); // pass by value 傳值
      std::thread t3(f2, std::ref(n)); // pass by reference  傳引用
     
      std::this_thread::sleep_for(std::chrono::milliseconds(2000));
      std::cout << "\nThread 4 create :\n";
      std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread 這時候t3將不是線程,t4接替t3繼續運行f2
      t2.join();
      t4.join();
      std::cout << "Final value of n is " << n << '\n';
    }

    線程的創建方式:

    • (1). 默認構造函數,創建一個空的 thread 執行對象。

    • (2). 初始化構造函數,創建一個 thread對象,該 thread對象可被 joinable,新產生的線程會調用 fn 函數,該函數的參數由 args 給出。

    • (3). 拷貝構造函數(被禁用),意味著 thread 不可被拷貝構造。

    • (4). move 構造函數,move 構造函數,調用成功之后 x 不代表任何 thread 執行對象。

    • 注意:可被 joinable 的 thread 對象必須在他們銷毀之前被主線程 join 或者將其設置為 detached.

    std::thread定義一個線程對象,傳入線程所需要的線程函數和參數,線程自動開啟

    線程的結束方式:

    • join()

    創建線程執行線程函數,調用該函數會阻塞當前線程,直到線程執行完join才返回;等待t線程結束,當前線程繼續往下運行

    • detach()

    detach調用之后,目標線程就成為了守護線程,駐留后臺運行,與之關聯的std::thread對象失去對目標線程的關聯,無法再通過std::thread對象取得該線程的控制權,由操作系統負責回收資源;主線程結束,整個進程結束,所有子線程都自動結束了!

    #include <iostream>
    #include <thread>
    using namespace std;
     
    void threadHandle1(int time)
    {
    	//讓子線程睡眠time秒
    	std::this_thread::sleep_for(std::chrono::seconds(time));
    	cout << "hello thread1!" << endl;
    }
    void threadHandle2(int time)
    {
    	//讓子線程睡眠time秒ace this_thread是namespace 
    	std::this_thread::sleep_for(std::chrono::seconds(time));
    	cout << "hello thread2!" << endl;
    }
    int main()
    {
    	//創建了一個線程對象,傳入一個線程函數(作為線程入口函數),
    	//新線程就開始運行了,沒有先后順序,隨著CPU的調度算法執行 
    	std::thread t1(threadHandle1, 2);
    	std::thread t2(threadHandle2, 3);
     
    	//主線程(main)運行到這里,等待子線程結束,主線程才繼續往下運行
    	t1.join();
    	t2.join();
     
    	//把子線程設置為分離線程,子線程和主線程就毫無關系了
    	//主線程結束的時候查看其他線程
    	//但是這個子線程運行完還是沒運行完都和這個主線程沒關系了
    	//這個子線程就從這個main分離出去了
    	//運行程序時也看不到這個子線程的任何輸出打印了
        //t1.detach();
     
    	cout << "main thread done!" << endl;
     
    	//主線程運行完成,查看如果當前進程還有未運行完成的子線程
    	//進程就會異常終止
    	return 0;
    }

    2 互斥鎖

    Mutex 又稱互斥量,C++ 11中與 Mutex 相關的類(包括鎖類型)和函數都聲明在 <mutex> 頭文件中,所以如果你需要使用 std::mutex,就必須包含 <mutex> 頭文件。

    <mutex> 頭文件介紹

    Mutex 系列類(四種)

    • std::mutex,最基本的 Mutex 類。

    • std::recursive_mutex,遞歸 Mutex 類。

    • std::time_mutex,定時 Mutex 類。

    • std::recursive_timed_mutex,定時遞歸 Mutex 類。

    Lock 類(兩種)

    • std::lock_guard,與 Mutex RAII 相關,方便線程對互斥量上鎖。

    • std::unique_lock,與 Mutex RAII 相關,方便線程對互斥量上鎖,但提供了更好的上鎖和解鎖控制。

    其他類型

    std::once_flagstd::adopt_lock_tstd::defer_lock_tstd::try_to_lock_t

    函數

    • std::try_lock,嘗試同時對多個互斥量上鎖。

    • std::lock,可以同時對多個互斥量上鎖。

    • std::call_once,如果多個線程需要同時調用某個函數,call_once 可以保證多個線程對該函數只調用一次。

    std::mutex 介紹

    下面以 std::mutex 為例介紹 C++11 中的互斥量用法。

    std::mutex 是C++11 中最基本的互斥量,std::mutex 對象提供了獨占所有權的特性——即不支持遞歸地對 std::mutex 對象上鎖,而 std::recursive_lock 則可以遞歸地對互斥量對象上鎖。

    std::mutex 的成員函數

    • 構造函數,std::mutex不允許拷貝構造,也不允許 move 拷貝,最初產生的 mutex 對象是處于 unlocked 狀態的。

    • lock(),調用線程將鎖住該互斥量。線程調用該函數會發生下面 3 種情況:(1). 如果該互斥量當前沒有被鎖住,則調用線程將該互斥量鎖住,直到調用 unlock之前,該線程一直擁有該鎖。(2). 如果當前互斥量被其他線程鎖住,則當前的調用線程被阻塞住。(3). 如果當前互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)。

    • unlock(), 解鎖,釋放對互斥量的所有權。

    • try_lock(),嘗試鎖住互斥量,如果互斥量被其他線程占有,則當前線程也不會被阻塞。線程調用該函數也會出現下面 3 種情況,(1). 如果當前互斥量沒有被其他線程占有,則該線程鎖住互斥量,直到該線程調用 unlock 釋放互斥量。(2). 如果當前互斥量被其他線程鎖住,則當前調用線程返回 false,而并不會被阻塞掉。(3). 如果當前互斥量被當前調用線程鎖住,則會產生死鎖(deadlock)

            為了保證lock()和unlock()對應使用,一般不直接使用mutex,而是和lock_guard、unique_lock一起使用;

    std::lock_guard

    std::lock_guard是RAII模板類的簡單實現,功能簡單。

    1.std::lock_guard 在構造函數中進行加鎖,析構函數中進行解鎖。

    		// CLASS TEMPLATE lock_guard
    template<class _Mutex>
    	class lock_guard
    	{	// class with destructor that unlocks a mutex
    public:
    	using mutex_type = _Mutex;
     
    	explicit lock_guard(_Mutex& _Mtx)
    		: _MyMutex(_Mtx)
    		{	// construct and lock
    		_MyMutex.lock();
    		}
     
    	lock_guard(_Mutex& _Mtx, adopt_lock_t)
    		: _MyMutex(_Mtx)
    		{	// construct but don't lock
    		}
     
    	~lock_guard() noexcept
    		{	// unlock
    		_MyMutex.unlock();
    		}
     
    	lock_guard(const lock_guard&) = delete;
    	lock_guard& operator=(const lock_guard&) = delete;
    private:
    	_Mutex& _MyMutex;
    	};

    從lock_guard源碼可以看出,它在構造時進行上鎖,在出作用域執行析構函數釋放鎖;同時不允許拷貝構造和賦值運算符;比較簡單,不能用在函數參數傳遞或者返回過程中,因為它的拷貝構造和賦值運算符被禁用了;只能用在簡單的臨界區代碼的互斥操作

    std::unique_lock

    類 unique_lock 是通用互斥包裝器,允許延遲鎖定、鎖定的有時限嘗試、遞歸鎖定、所有權轉移和與條件變量一同使用
    unique_lock比lock_guard使用更加靈活,功能更加強大。
    使用unique_lock需要付出更多的時間、性能成本。

    template<class _Mutex>
    	class unique_lock
    	{	// whizzy class with destructor that unlocks mutex
    public:
    	typedef _Mutex mutex_type;
     
    	// CONSTRUCT, ASSIGN, AND DESTROY
    	unique_lock() noexcept
    		: _Pmtx(nullptr), _Owns(false)
    		{	// default construct
    		}
     
    	explicit unique_lock(_Mutex& _Mtx)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
    		{	// construct and lock
    		_Pmtx->lock();
    		_Owns = true;
    		}
     
    	unique_lock(_Mutex& _Mtx, adopt_lock_t)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(true)
    		{	// construct and assume already locked
    		}
     
    	unique_lock(_Mutex& _Mtx, defer_lock_t) noexcept
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
    		{	// construct but don't lock
    		}
     
    	unique_lock(_Mutex& _Mtx, try_to_lock_t)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock())
    		{	// construct and try to lock
    		}
     
    	template<class _Rep,
    		class _Period>
    		unique_lock(_Mutex& _Mtx,
    			const chrono::duration<_Rep, _Period>& _Rel_time)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_for(_Rel_time))
    		{	// construct and lock with timeout
    		}
     
    	template<class _Clock,
    		class _Duration>
    		unique_lock(_Mutex& _Mtx,
    			const chrono::time_point<_Clock, _Duration>& _Abs_time)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time))
    		{	// construct and lock with timeout
    		}
     
    	unique_lock(_Mutex& _Mtx, const xtime *_Abs_time)
    		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
    		{	// try to lock until _Abs_time
    		_Owns = _Pmtx->try_lock_until(_Abs_time);
    		}
     
    	unique_lock(unique_lock&& _Other) noexcept
    		: _Pmtx(_Other._Pmtx), _Owns(_Other._Owns)
    		{	// destructive copy
    		_Other._Pmtx = nullptr;
    		_Other._Owns = false;
    		}
     
    	unique_lock& operator=(unique_lock&& _Other)
    		{	// destructive copy
    		if (this != _STD addressof(_Other))
    			{	// different, move contents
    			if (_Owns)
    				_Pmtx->unlock();
    			_Pmtx = _Other._Pmtx;
    			_Owns = _Other._Owns;
    			_Other._Pmtx = nullptr;
    			_Other._Owns = false;
    			}
    		return (*this);
    		}
     
    	~unique_lock() noexcept
    		{	// clean up
    		if (_Owns)
    			_Pmtx->unlock();
    		}
     
    	unique_lock(const unique_lock&) = delete;
    	unique_lock& operator=(const unique_lock&) = delete;
     
    	void lock()
    		{	// lock the mutex
    		_Validate();
    		_Pmtx->lock();
    		_Owns = true;
    		}
     
    	_NODISCARD bool try_lock()
    		{	// try to lock the mutex
    		_Validate();
    		_Owns = _Pmtx->try_lock();
    		return (_Owns);
    		}
     
    	template<class _Rep,
    		class _Period>
    		_NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time)
    		{	// try to lock mutex for _Rel_time
    		_Validate();
    		_Owns = _Pmtx->try_lock_for(_Rel_time);
    		return (_Owns);
    		}
     
    	template<class _Clock,
    		class _Duration>
    		_NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time)
    		{	// try to lock mutex until _Abs_time
    		_Validate();
    		_Owns = _Pmtx->try_lock_until(_Abs_time);
    		return (_Owns);
    		}
     
    	_NODISCARD bool try_lock_until(const xtime *_Abs_time)
    		{	// try to lock the mutex until _Abs_time
    		_Validate();
    		_Owns = _Pmtx->try_lock_until(_Abs_time);
    		return (_Owns);
    		}
     
    	void unlock()
    		{	// try to unlock the mutex
    		if (!_Pmtx || !_Owns)
    			_THROW(system_error(
    				_STD make_error_code(errc::operation_not_permitted)));
     
    		_Pmtx->unlock();
    		_Owns = false;
    		}
     
    	void swap(unique_lock& _Other) noexcept
    		{	// swap with _Other
    		_STD swap(_Pmtx, _Other._Pmtx);
    		_STD swap(_Owns, _Other._Owns);
    		}
     
    	_Mutex *release() noexcept
    		{	// disconnect
    		_Mutex *_Res = _Pmtx;
    		_Pmtx = nullptr;
    		_Owns = false;
    		return (_Res);
    		}
     
    	_NODISCARD bool owns_lock() const noexcept
    		{	// return true if this object owns the lock
    		return (_Owns);
    		}
     
    	explicit operator bool() const noexcept
    		{	// return true if this object owns the lock
    		return (_Owns);
    		}
     
    	_NODISCARD _Mutex *mutex() const noexcept
    		{	// return pointer to managed mutex
    		return (_Pmtx);
    		}
     
    private:
    	_Mutex *_Pmtx;
    	bool _Owns;
     
    	void _Validate() const
    		{	// check if the mutex can be locked
    		if (!_Pmtx)
    			_THROW(system_error(
    				_STD make_error_code(errc::operation_not_permitted)));
     
    		if (_Owns)
    			_THROW(system_error(
    				_STD make_error_code(errc::resource_deadlock_would_occur)));
    		}
    	};

    其中,有_Mutex *_Pmtx; 指向一把鎖的指針;不允許使用左值拷貝構造和賦值,但是可以使用右值拷貝構造和賦值,可以在函數調用過程中使用。因此可以和條件變量一起使用:cv.wait(lock);//可以作為函數參數傳入;

    示例:

    在多線程環境中運行的代碼段,需要考慮是否存在競態條件,如果存在競態條件,我們就說該代碼段不是線程安全的,不能直接運行在多線程環境當中,對于這樣的代碼段,我們經常稱之為臨界區資源,對于臨界區資源,多線程環境下需要保證它以原子操作執行,要保證臨界區的原子操作,就需要用到線程間的互斥操作-鎖機制,thread類庫還提供了更輕量級的基于CAS操作的原子操作類。

    無鎖時:

    #include <iostream>
    #include <atomic>//C++11線程庫提供的原子類
    #include <thread>//C++線程類庫的頭文件
    #include <vector>
     
     
    int count = 0;
     
    //線程函數
    void sumTask()
    {
      
      //每個線程給count加10次
      for (int i = 0; i < 10; ++i)
      {
        count++;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
      }
    }
     
    int main()
    {
      //創建10個線程放在容器當中
      std::vector<std::thread> vec;
      for (int i = 0; i < 10; ++i)
      {
        vec.push_back(std::thread(sumTask));
      }
     
      //等待線程執行完成
      for (unsigned int i = 0; i < vec.size(); ++i)
      {
        vec[i].join();
      }
     
      //所有子線程運行結束
      std::cout << "count : " << count << std::endl;
     
      return 0;
    }

    多線程同時對count進行操作,并不能保證同時只有一個線程對count執行++操作,最后的的結果不一定是100;

    使用lock_guard:

    #include <iostream>
    #include <atomic>//C++11線程庫提供的原子類
    #include <thread>//C++線程類庫的頭文件
    #include <mutex>
    #include <vector>
     
     
    int count = 0;
    std::mutex mutex;
     
    //線程函數
    void sumTask()
    {
      
      //每個線程給count加10次
      for (int i = 0; i < 10; ++i)
      {
        {
          std::lock_guard<std::mutex> lock(mutex);
          count++;
        }
    ;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
      }
    }
     
    int main()
    {
      //創建10個線程放在容器當中
      std::vector<std::thread> vec;
      for (int i = 0; i < 10; ++i)
      {
        vec.push_back(std::thread(sumTask));
      }
     
      //等待線程執行完成
      for (unsigned int i = 0; i < vec.size(); ++i)
      {
        vec[i].join();
      }
     
      //所有子線程運行結束,count的結果每次運行應該都是10000
      std::cout << "count : " << count << std::endl;
     
      return 0;
    }

    對count++ 操作上鎖,保證一次只有一個線程能對其操作,結果是100

    原子變量

    上面的保證原子操作需要在多線程環境下添加互斥操作,但是mutex互斥鎖畢竟比較重,對于系統消耗有些大,C++11的thread類庫提供了針對簡單類型的原子操作類,如std::atomic_int,atomic_long,atomic_bool等,它們值的增減都是基于CAS操作的,既保證了線程安全,效率還非常高。

    #include <iostream>
    #include <atomic>//C++11線程庫提供的原子類
    #include <thread>//C++線程類庫的頭文件
    #include <vector>
     
    //原子整型,CAS操作保證給count自增自減的原子操作
    std::atomic_int count = 0;
     
    //線程函數
    void sumTask()
    {
      //每個線程給count加10次
      for (int i = 0; i < 10; ++i)
      {
        count++;
      }
    }
     
    int main()
    {
      //創建10個線程放在容器當中
      std::vector<std::thread> vec;
      for (int i = 0; i < 10; ++i)
      {
        vec.push_back(std::thread(sumTask));
      }
     
      //等待線程執行完成
      for (unsigned int i = 0; i < vec.size(); ++i)
      {
        vec[i].join();
      }
     
      //所有子線程運行結束,count的結果每次運行應該都是10000
      std::cout << "count : " << count << std::endl;
     
      return 0;
    }

    線程同步通信

            多線程在運行過程中,各個線程都是隨著OS的調度算法,占用CPU時間片來執行指令做事情,每個線程的運行完全沒有順序可言。但是在某些應用場景下,一個線程需要等待另外一個線程的運行結果,才能繼續往下執行,這就需要涉及線程之間的同步通信機制。

            線程間同步通信最典型的例子就是生產者-消費者模型,生產者線程生產出產品以后,會通知消費者線程去消費產品;如果消費者線程去消費產品,發現還沒有產品生產出來,它需要通知生產者線程趕快生產產品,等生產者線程生產出產品以后,消費者線程才能繼續往下執行。

    C++11 線程庫提供的條件變量condition_variable,就是Linux平臺下的Condition Variable機制,用于解決線程間的同步通信問題,下面通過代碼演示一個生產者-消費者線程模型:    

    #include <iostream>           //std::cout
    #include <thread>             //std::thread
    #include <mutex>              //std::mutex, std::unique_lock
    #include <condition_variable> //std::condition_variable
    #include <vector>
     
    //定義互斥鎖(條件變量需要和互斥鎖一起使用)
    std::mutex mtx;
    //定義條件變量(用來做線程間的同步通信)
    std::condition_variable cv;
    //定義vector容器,作為生產者和消費者共享的容器
    std::vector<int> vec;
     
    //生產者線程函數
    void producer()
    {
      //生產者每生產一個,就通知消費者消費一個
      for (int i = 1; i <= 10; ++i)
      {
        //獲取mtx互斥鎖資源
        std::unique_lock<std::mutex> lock(mtx);
     
        //如果容器不為空,代表還有產品未消費,等待消費者線程消費完,再生產
        while (!vec.empty())
        {
          //判斷容器不為空,進入等待條件變量的狀態,釋放mtx鎖,
          //讓消費者線程搶到鎖能夠去消費產品
          cv.wait(lock);
        }
        vec.push_back(i); // 表示生產者生產的產品序號i
        std::cout << "producer生產產品:" << i << std::endl;
     
        /*
        生產者線程生產完產品,通知等待在cv條件變量上的消費者線程,
        可以開始消費產品了,然后釋放鎖mtx
        */
        cv.notify_all();
     
        //生產一個產品,睡眠100ms
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
    }
    //消費者線程函數
    void consumer()
    {
      //消費者每消費一個,就通知生產者生產一個
      for (int i = 1; i <= 10; ++i)
      {
        //獲取mtx互斥鎖資源
        std::unique_lock<std::mutex> lock(mtx);
     
        //如果容器為空,代表還有沒有產品可消費,等待生產者生產,再消費
        while (vec.empty())
        {
          //判斷容器為空,進入等待條件變量的狀態,釋放mtx鎖,
          //讓生產者線程搶到鎖能夠去生產產品
          cv.wait(lock);
        }
        int data = vec.back(); // 表示消費者消費的產品序號i
        vec.pop_back();
        std::cout << "consumer消費產品:" << data << std::endl;
     
        /*
        消費者消費完產品,通知等待在cv條件變量上的生產者線程,
        可以開始生產產品了,然后釋放鎖mtx
        */
        cv.notify_all();
     
        //消費一個產品,睡眠100ms
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
    }
    int main()
    {
      //創建生產者和消費者線程
      std::thread t1(producer);
      std::thread t2(consumer);
     
      //main主線程等待所有子線程執行完
      t1.join();
      t2.join();
     
      return 0;
    }

    線程死鎖

    死鎖概述

    線程死鎖是指兩個或兩個以上的線程互相持有對方所需要的資源,由于synchronized的特性,一個線程持有一個資源,或者說獲得一個鎖,在該線程釋放這個鎖之前,其它線程是獲取不到這個鎖的,而且會一直死等下去,因此這便造成了死鎖。

    死鎖產生的條件

    • 互斥條件:一個資源,或者說一個鎖只能被一個線程所占用,當一個線程首先獲取到這個鎖之后,在該線程釋放這個鎖之前,其它線程均是無法獲取到這個鎖的。

    • 占有且等待:一個線程已經獲取到一個鎖,再獲取另一個鎖的過程中,即使獲取不到也不會釋放已經獲得的鎖。

    • 不可剝奪條件:任何一個線程都無法強制獲取別的線程已經占有的鎖

    • 循環等待條件:線程A拿著線程B的鎖,線程B拿著線程A的鎖。

    示例:
    當一個程序的多個線程獲取多個互斥鎖資源的時候,就有可能發生死鎖問題,比如線程A先獲取了鎖1,線程B獲取了鎖2,進而線程A還需要獲取鎖2才能繼續執行,但是由于鎖2被線程B持有還沒有釋放,線程A為了等待鎖2資源就阻塞了;線程B這時候需要獲取鎖1才能往下執行,但是由于鎖1被線程A持有,導致A也進入阻塞。

    線程A和線程B都在等待對方釋放鎖資源,但是它們又不肯釋放原來的鎖資源,導致線程A和B一直互相等待,進程死鎖了。下面代碼示例演示這個問題:

    #include <iostream>           //std::cout
    #include <thread>             //std::thread
    #include <mutex>              //std::mutex, std::unique_lock
    #include <condition_variable> //std::condition_variable
    #include <vector>
     
    //鎖資源1
    std::mutex mtx1;
    //鎖資源2
    std::mutex mtx2;
     
    //線程A的函數
    void taskA()
    {
      //保證線程A先獲取鎖1
      std::lock_guard<std::mutex> lockA(mtx1);
      std::cout << "線程A獲取鎖1" << std::endl;
     
      //線程A睡眠2s再獲取鎖2,保證鎖2先被線程B獲取,模擬死鎖問題的發生
      std::this_thread::sleep_for(std::chrono::seconds(2));
     
      //線程A先獲取鎖2
      std::lock_guard<std::mutex> lockB(mtx2);
      std::cout << "線程A獲取鎖2" << std::endl;
     
      std::cout << "線程A釋放所有鎖資源,結束運行!" << std::endl;
    }
     
    //線程B的函數
    void taskB()
    {
      //線程B先睡眠1s保證線程A先獲取鎖1
      std::this_thread::sleep_for(std::chrono::seconds(1));
      std::lock_guard<std::mutex> lockB(mtx2);
      std::cout << "線程B獲取鎖2" << std::endl;
     
      //線程B嘗試獲取鎖1
      std::lock_guard<std::mutex> lockA(mtx1);
      std::cout << "線程B獲取鎖1" << std::endl;
     
      std::cout << "線程B釋放所有鎖資源,結束運行!" << std::endl;
    }
    int main()
    {
      //創建生產者和消費者線程
      std::thread t1(taskA);
      std::thread t2(taskB);
     
      //main主線程等待所有子線程執行完
      t1.join();
      t2.join();
     
      return 0;
    }

    輸出:

    C++11中thread多線程編程如何創建

    可以看到,線程A獲取鎖1、線程B獲取鎖2以后,進程就不往下繼續執行了,一直等待在這里,如果這是我們碰到的一個問題場景,我們如何判斷出這是由于線程間死鎖引起的呢?

    打開process Explorer.找到該進程,查看線程狀態,發現線程的cpu利用率為0,那么應該不是死循環,應該是死鎖了: 

    C++11中thread多線程編程如何創建 

    點擊vs 的全部中斷:查看每一個線程的函數執行的位置

    C++11中thread多線程編程如何創建

     發現當前線程正在申請鎖的位置,判斷出應該是鎖了。

    C++11中thread多線程編程如何創建

     同時主線程走了等待子線程結束;

    C++11中thread多線程編程如何創建

    那如果是死循環的情況呢?,如將線程2加一個死循環:

    #include <iostream>           //std::cout
    #include <thread>             //std::thread
    #include <mutex>              //std::mutex, std::unique_lock
    #include <condition_variable> //std::condition_variable
    #include <vector>
     
    //鎖資源1
    std::mutex mtx1;
    //鎖資源2
    std::mutex mtx2;
     
    //線程A的函數
    void taskA()
    {
      //保證線程A先獲取鎖1
      std::lock_guard<std::mutex> lockA(mtx1);
      std::cout << "線程A獲取鎖1" << std::endl;
     
      //線程A睡眠2s再獲取鎖2,保證鎖2先被線程B獲取,模擬死鎖問題的發生
      std::this_thread::sleep_for(std::chrono::seconds(2));
     
      //線程A先獲取鎖2
      std::lock_guard<std::mutex> lockB(mtx2);
      std::cout << "線程A獲取鎖2" << std::endl;
     
      std::cout << "線程A釋放所有鎖資源,結束運行!" << std::endl;
    }
     
    //線程B的函數
    void taskB()
    {
      while (true)
      {
     
      }
    }
    int main()
    {
      //創建生產者和消費者線程
      std::thread t1(taskA);
      std::thread t2(taskB);
     
      //main主線程等待所有子線程執行完
      t1.join();
      t2.join();
     
      return 0;
    }

    C++11中thread多線程編程如何創建 

     這時候工作線程占滿了CPU,我的電腦是8核,因此占滿一個cpu是12.5%

    以上是“C++11中thread多線程編程如何創建”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    剑川县| 偃师市| 大埔县| 聂荣县| 银川市| 大兴区| 灯塔市| 阜康市| 富民县| 利川市| 延庆县| 长汀县| 黔南| 德安县| 阳高县| 岳阳市| 峨边| 龙江县| 阿鲁科尔沁旗| 仁寿县| 阜城县| 苏州市| 高陵县| 基隆市| 普洱| 庆城县| 西乡县| 阜康市| 资阳市| 蒙阴县| 三亚市| 游戏| 陆川县| 八宿县| 溧阳市| 岫岩| 勐海县| 新干县| 泰来县| 亚东县| 吉首市|