日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

LES06 :C++线程与智能指针

發布時間:2023/12/20 c/c++ 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LES06 :C++线程与智能指针 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C++線程與智能指針

文章目錄

  • C++線程與智能指針
    • 線程
      • C++11線程
      • POSIX線程
        • 線程屬性
          • 分離線程
          • 調度策略與優先級
        • 線程同步
        • 條件變量
    • 智能指針
      • shared_ptr
        • weak_ptr
      • unique_ptr
      • 自定義智能指針
    • 部分C++11、14特性
      • nullptr
      • 類型推導
      • 基于范圍的 for 循環
      • Lambda
    • 作業(后面FFmpeg直播播放器課程的隊列工具類)

線程

線程,有時被稱為輕量進程,是程序執行的最小單元。

C++11線程

#include <thread>void task(int i) {cout << "task:" << i << endl; }thread t1(task,100); //等待線程結束再繼續執行 t1.join();

POSIX線程

POSIX 可移植操作系統接口,標準定義了操作系統應該為應用程序提供的接口標準

Windows上使用 配置: 下載

cmake_minimum_required (VERSION 3.8) include_directories("XXX/pthreads-w32-2-9-1-release/Pre-built.2/include") #設置變量為x64 or x86 if(CMAKE_CL_64)set(platform x64) else()set(platform x86) endif() link_directories("XXX/pthreads-w32-2-9-1-release/Pre-built.2/lib/${platform}") #如果出現 “timespec”:“struct” 類型重定義 設置 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_STRUCT_TIMESPEC") # 將源添加到此項目的可執行文件。 add_executable (lsn6example "lsn6_example.cpp" "lsn6_example.h") target_link_libraries(lsn6example pthreadVC2)

32位拷貝pthreadVC2.dll 到windows/syswow64目錄

64位拷貝pthreadVC2.dll 到windows/system32目錄

#include <pthread.h> void *pthreadTask(void* args) {int* i = static_cast<int*>(args);cout << "posix線程:" << *i << endl;return 0; } pthread_t pid; int pi = 100; pthread_create(&pid, 0, pthreadTask, &pi); //等待線程的結束 pthread_join(pid,0);

線程屬性

線程具有屬性,用 pthread_attr_t 表示

pthread_attr_t attr; //初始化 attr中為操作系統實現支持的線程所有屬性的默認值 pthread_attr_init(&attr); pthread_attr_destroy(&attr);
分離線程

線程創建默認是非分離的,當pthread_join()函數返回時,創建的線程終止,釋放自己占用的系統資源

分離線程不能被其他線程等待,pthread_join無效,線程自己玩自己的。

//設置是否為分離線程 //PTHREAD_CREATE_DETACHED 分離 //PTHREAD_CREATE_JOINABLE 非分離 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
調度策略與優先級

Windows 無法設置成功

//設置調度策略 //返回0 設置成功 pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // SCHED_FIFO // 實時調度策略,先到先服務 一旦占用cpu則一直運行。一直運行直到有更高優先級任務到達或自己放棄。 // SCHED_RR // 實時調度策略,時間輪轉 系統分配一個時間段,在時間段內執行本線程//設置優先級 //獲得對應策略的最小、最大優先級 int max = sched_get_priority_max(SCHED_FIFO); int min = sched_get_priority_min(SCHED_FIFO); sched_param param; param.sched_priority = max; pthread_attr_setschedparam(&attr, &param);

線程同步

多線程同時讀寫同一份共享資源的時候,可能會引起沖突。需要引入線程“同步”機制,即各位線程之間有序地對共享資源進行操作。

#include <pthread.h> using namespace std;queue<int> q; void *pop(void* args) {//線程未同步導致的多線程安全問題// 會有重復的數據取出并出現異常if (!q.empty()){printf("取出數據:%d\n", q.front());q.pop();}else {printf("無數據\n");}return 0; }int main() {for (size_t i = 0; i < 5; i++){q.push(i);}pthread_t pid[10];for (size_t i = 0; i < 10; i++){pthread_create(&pid[i], 0, pop, &q);}system("pause");return 0; }

加入互斥鎖

queue<int> q; pthread_mutex_t mutex; void *pop(void* args) {// 鎖pthread_mutex_lock(&mutex);if (!q.empty()){printf("取出數據:%d\n", q.front());q.pop();}else {printf("無數據\n");}// 放pthread_mutex_unlock(&mutex);return 0; }int main() {//初始化互斥鎖pthread_mutex_init(&mutex, 0);for (size_t i = 0; i < 5; i++){q.push(i);}pthread_t pid[10];for (size_t i = 0; i < 10; i++){pthread_create(&pid[i], 0, pop, &q);}//需要釋放for (size_t i = 0; i < 10; i++){pthread_join(pid[i], 0);}pthread_mutex_destroy(&mutex);system("pause");return 0; }

條件變量

條件變量是線程間進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立",從而喚醒掛起線程

template <class T> class SafeQueue { public:SafeQueue() {pthread_mutex_init(&mutex,0);}~SafeQueue() {pthread_mutex_destory(&mutex);}void enqueue(T t) {pthread_mutex_lock(&mutex);q.push(t);pthread_mutex_unlock(&mutex);}int dequeue(T& t) {pthread_mutex_lock(&mutex);if (!q.empty()){t = q.front();q.pop();pthread_mutex_unlock(&mutex);return 1;}pthread_mutex_unlock(&mutex);return 0;}private:queue<T> q;pthread_mutex_t mutex; };

上面的模板類存放數據T,并使用互斥鎖保證對queue的操作是線程安全的。這就是一個生產/消費模式。

如果在取出數據的時候,queue為空,則一直等待,直到下一次enqueue加入數據。

這就是一個典型的生產/消費模式, 加入條件變量使 “dequeue” 掛起,直到由其他地方喚醒

#pragma once #include <queue> using namespace std;template <class T> class SafeQueue { public:SafeQueue() {pthread_mutex_init(&mutex,0);pthread_cond_init(&cond, 0);}~SafeQueue() {pthread_mutex_destory(&mutex);pthread_cond_destory(&cond);}void enqueue(T t) {pthread_mutex_lock(&mutex);q.push(t);//發出信號 通知掛起線程//由系統喚醒一個線程//pthread_cond_signal(&cond);// 廣播 對應多個消費者的時候 多個線程等待喚醒所有pthread_cond_broadcast(&cond);pthread_mutex_unlock(&mutex);}int dequeue(T& t) {pthread_mutex_lock(&mutex);//可能被意外喚醒 所以while循環while (q.empty()){pthread_cond_wait(&cond, &mutex);}t = q.front();q.pop();pthread_mutex_unlock(&mutex);return 1;}private:queue<T> q;pthread_mutex_t mutex;pthread_cond_t cond; }; #include "lsn6_example.h" #include <thread> #include <pthread.h>using namespace std; #include "safe_queue.h"SafeQueue<int> q;void *get(void* args) {while (1) {int i;q.dequeue(i);cout << "消費:"<< i << endl;}return 0; } void *put(void* args) {while (1){int i;cin >> i;q.enqueue(i);}return 0; } int main() {pthread_t pid1, pid2;pthread_create(&pid1, 0, get, &q);pthread_create(&pid2, 0, put, &q);pthread_join(pid2,0);system("pause");return 0; }

智能指針

自C++11起,C++標準庫提供了兩大類型的智能指針

shared_ptr

操作引用計數實現共享式擁有的概念。多個智能指針可以指向相同的對象,這個對象和其相關資源會在最后一個被銷毀時釋放。

class A { public:~A() {cout << "釋放A" << endl;} };void test() {//自動釋放 引用計數為1shared_ptr<A> a(new A());//退出方法 shared_ptr a本身釋放,對內部的 A 對象引用計數減1 則為0 釋放new 出來的A 對象 }

雖然使用shared_ptr能夠非常方便的為我們自動釋放對象,但是還是會出現一些問題。最典型的就是循環引用問題。

class B; class A { public:~A() {cout << "釋放A" << endl;}shared_ptr<B> b; };class B { public:~B() {cout << "釋放B" << endl;}shared_ptr<A> a; }; void test() {//自動釋放shared_ptr<A> a(new A()); //A引用計數為1shared_ptr<B> b(new B()); //B引用計數為1cout << a.use_count() << endl; //查看內部對象引用計數a->b = b; //A 引用計數為2b->a = a; //B 引用計數為2//退出方法,a釋放,A引用計數-1結果為1 不會釋放 B也一樣 }

weak_ptr

weak_ptr是為配合shared_ptr而引入的一種智能指針。主要用于觀測資源的引用情況。

它的構造和析構不會引起引用記數的增加或減少。沒有重載*和->但可以使用lock獲得一個可用的shared_ptr對象。

配合shared_ptr解決循環引用問題

class B; class A { public:~A() {cout << "釋放A" << endl;}weak_ptr<B> b; }; class B { public:~B() {cout << "釋放B" << endl;}weak_ptr<A> a; };void test() {//自動釋放shared_ptr<A> a(new A()); //A引用計數為1shared_ptr<B> b(new B()); //B引用計數為1a->b = b; //weak_ptr 引用計數不增加b->a = a; //weak_ptr 引用計數不增加//退出方法,A B釋放 }

weak_ptr 提供expired 方法等價于 use_count == 0,當expired為true時,lock返回一個存儲空指針的shared_ptr

unique_ptr

實現獨占式引用,保證同一時間只有一個智能指針指向內部對象。

unique_ptr<A> a(new A());

auto_ptr已經不推薦使用

自定義智能指針

template <typename T> class Ptr { public:Ptr() {count = new int(1);t = 0;}Ptr(T *t):t(t) {//引用計數為1count = new int(1);}~Ptr() {//引用計數-1 為0表示可以釋放T了if (--(*count) == 0){if (t) {delete t;}delete count;t = 0;count = 0;}}//拷貝構造函數Ptr(const Ptr<T> &p) {//引用計數+1++(*p.count);t = p.t;count = p.count;}Ptr<T>& operator=(const Ptr<T>& p) {++(*p.count);//檢查老的數據是否需要刪除if (--(*count) == 0) {if (t) {delete t;}delete count;}t = p.t;count = p.count;return *this;}//重載-> 操作T 類T* operator->() { return t; }private:T *t;int *count; };

重載=為什么返回引用,而不是對象?

return *this后馬上就調用拷貝構造函數,將*this拷貝給一個匿名臨時對象,然后在把臨時對象拷貝給外部的左值(a=b,a為左值),再釋放臨時對象。這樣首先會造成不必要的開銷。

同時如果沒有自定義的拷貝函數,則會使用默認的淺拷貝。如果類中存在指向堆空間的成員指針,需要進行深拷貝(重新申請內存),避免相同內存地址被其他地方釋放導致的問題。

如果此處返回對象不會導致出現問題:

第四節課中提到:

引用類型(Test1&) 沒有復制對象 返回的是 t 對象本身 t會被釋放 所以會出現問題(數據釋放不徹底就不一定)

這里的t作用域是函數內我們自己創建的一個臨時對象,因此會被釋放,而在此處返回 *this,作用范圍和t不一樣。其次,在類中定義了拷貝函數,雖然未進行深拷貝,但小心的維護了堆內存指針(t、count),不會出現堆內存釋放導致的懸空指針情況。

部分C++11、14特性

nullptr

nullptr 出現的目的是為了替代 NULL。 C++11之前直接將NULL定義為 0。

void test(int* i){} void test(int i){} //現在調用哪一個test? test(int) test(NULL); //調用test(int* i) test(nullptr);

類型推導

C++11 重新定義了auto 和 decltype 這兩個關鍵字實現了類型推導,讓編譯器來操心變量的類型。

auto i = 5; // i 被推導為 int auto p = new auto(10) // arr 被推導為 int * //但是auto不能作用在數組上 auto arr1[10] = { 0 }; //錯誤 auto arr2= {0}; //正確 typeid(arr2).name() //獲得類型名為 initializer_list(后續介紹) // int j decltype(i) j = 10;

基于范圍的 for 循環

實際上就是foreach

vector<int> vec = { 1,2,3,4,5 }; //配合auto使用 for(auto i : vec) {cout << i << endl; }

Lambda

匿名函數,即沒有函數名的函數

完整形式:

[捕獲外部變量列表 ] (參數列表) mutable exception->返回類型 { 函數體 }

mutable:在外部變量列表以值來捕獲時,無法修改變量的值,加上mutable表示可修改(不會影響外部變量)

auto i = 5; // [&] 表示外部變量都以引用的形式在lambda中使用,函數內部修改i的值會影響外部 // 這里的 -> auto 自動推導在c++11不支持,c++14中對auto進行了擴展 thread t1([&] () -> auto {i = 100;cout << "線程:" << i << endl; }); _sleep(10); cout << i << endl; 捕獲形式說明
[]不捕獲任何外部變量
[i, …]以值得形式捕獲指定的多個外部變量(用逗號分隔);如果引用捕獲,需要顯示聲明&
[this]以值的形式捕獲this指針
[=]以值的形式捕獲所有外部變量
[&]以引用形式捕獲所有外部變量
[=, &x]變量x以引用形式捕獲,其余變量以傳值形式捕獲
[&, x]變量x以值的形式捕獲,其余變量以引用形式捕獲

作業(后面FFmpeg直播播放器課程的隊列工具類)

封裝queue滿足:

1、滿足同一個類存放AvFrame*與AvPacket*兩種結構

? 知識點:模板類

? AvFrame:存儲解碼的數據 創建:av_frame_alloc、釋放:av_frame_free

? AvPacket:存儲壓縮的數據 創建:av_packet_alloc、釋放:av_packet_free

2、滿足動態設置釋放函數(AvFrame與AvPacket釋放函數不同)

? 知識點:函數指針 函數引用傳值

3、保證線程安全同時取出數據時保證一定能夠取出

可用pts賦值 1、2、3…測試

總結

以上是生活随笔為你收集整理的LES06 :C++线程与智能指针的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。