線程的創建.銷毀

線程

線程的創建

在 C++ 中,線程的創建核心是通過std::thread類實現的,其構造函數需要傳入一個可調用對象(Callable Object)作為線程入口。可調用對象包括普通函數、lambda 表達式、函數對象(functor)、類的成員函數等。下面詳細介紹幾種常見的線程創建方式:

一、使用普通函數創建線程

最基礎的方式是將普通函數作為線程入口,可同時傳遞參數給函數。

#include <iostream>
#include <thread>// 普通函數:線程入口
void print_info(int thread_id, const std::string& message) {std::cout << "線程 " << thread_id << ": " << message << std::endl;
}int main() {// 創建線程:傳入函數名和參數(參數按順序傳遞)std::thread t1(print_info, 1, "Hello from thread 1");std::thread t2(print_info, 2, "Hello from thread 2");// 等待線程完成t1.join();t2.join();return 0;
}

說明

  • std::thread構造時,第一個參數是函數名,后續參數會被傳遞給該函數。
  • 若函數需要多個參數,直接在構造函數中按順序添加即可。

二、使用 lambda 表達式創建線程

lambda 表達式適合編寫簡短的線程邏輯,尤其當需要捕獲外部變量時非常方便。

#include <iostream>
#include <thread>int main() {int base = 100;  // 外部變量// 用lambda表達式創建線程(捕獲外部變量base)std::thread t([&base](int offset) {// 線程邏輯:使用捕獲的base和傳入的offsetstd::cout << "線程內計算:" << base + offset << std::endl;}, 50);  // 傳遞給lambda的參數(offset=50)t.join();  // 等待線程完成return 0;
}

說明

  • lambda 的捕獲列表([&base])用于訪問外部變量,&表示按引用捕獲(可修改外部變量),=表示按值捕獲(只讀)。
  • lambda 后的參數(如50)會作為 lambda 的輸入參數。

三、使用函數對象(Functor)創建線程

函數對象是重載了operator()的類 / 結構體,適合需要攜帶狀態(成員變量)的線程邏輯。

#include <iostream>
#include <thread>// 函數對象:重載operator()
struct Counter {int count;  // 攜帶的狀態// 構造函數初始化狀態Counter(int init) : count(init) {}// 線程入口:operator()void operator()(int step) {for (int i = 0; i < 5; ++i) {count += step;std::cout << "當前計數:" << count << std::endl;}}
};int main() {// 創建函數對象(初始狀態count=0)Counter counter(0);// 用函數對象創建線程,傳遞參數step=2std::thread t(std::ref(counter), 2);  // 注意用std::ref傳遞引用t.join();// 線程執行后,counter的狀態已被修改std::cout << "最終計數:" << counter.count << std::endl;return 0;
}

說明

  • 函數對象的成員變量(如count)可用于存儲線程的狀態,避免使用全局變量。
  • 若需在線程中修改原對象(而非副本),需用std::ref傳遞引用(否則std::thread會復制對象)。

四、使用類的成員函數創建線程

當線程邏輯需要訪問類的成員變量時,可將類的成員函數作為線程入口,需同時指定對象指針。

#include <iostream>
#include <thread>
#include <string>class Worker {
private:std::string name;  // 成員變量public:Worker(const std::string& n) : name(n) {}// 成員函數:線程入口void work(int task_id) {std::cout << "工人 " << name << " 正在執行任務 " << task_id << std::endl;}
};int main() {Worker worker("Alice");  // 創建對象// 用成員函數創建線程:參數為(對象指針,成員函數地址,函數參數)std::thread t(&Worker::work, &worker, 1001);  // &worker是對象指針t.join();return 0;
}

說明

  • std::thread構造時,第一個參數是成員函數地址(&Worker::work),第二個參數是對象指針(&worker),后續參數是成員函數的參數。
  • 若對象是動態分配的(new Worker(...)),則傳遞堆對象的指針即可。

關鍵注意事項

  1. 線程必須被 join 或 detach
    std::thread對象銷毀前,必須調用join()(等待線程結束)或detach()(分離線程,使其獨立運行),否則會觸發std::terminate()終止程序。

  2. 參數傳遞的拷貝問題
    線程構造時傳遞的參數會被拷貝到線程內部,若需傳遞引用,需用std::refstd::cref(常量引用),但需確保引用的對象生命周期長于線程。

  3. 線程入口的生命周期
    若線程入口是臨時對象(如 lambda 或函數對象),需確保其生命周期覆蓋線程執行期,避免懸空引用。

總結

C++ 線程創建的核心是通過std::thread綁定可調用對象,不同方式的適用場景:

  • 普通函數:適合簡單、無狀態的線程邏輯。
  • lambda 表達式:適合簡短邏輯或需要捕獲外部變量的場景。
  • 函數對象:適合需要攜帶狀態的復雜邏輯。
  • 成員函數:適合面向對象編程中,線程邏輯需訪問類成員的場景。

線程的銷毀

我們使用std::thread創建的線程對象是進程中的子線程,一般進程中還有主線程,在程序中就是main線程,那么當我們創建線程后至少是有兩個線程的,那么兩個線程誰先執行完畢誰后執行完畢,這是隨機的,但是當進程執行結束之后,主線程與子線程都會執行完畢,進程會回收線程擁有的資源。并且,主線程main執行完畢,其實整個進程也就執行完畢了。一般我們有兩種方式讓子線程結束,一種是主線程等待子線程執行完畢,我們使用join函數,讓主線程回收子線程的資源;另外一種是子線程與主線程分離,我們使用detach函數,此時子線程駐留在后臺運行,這個子線程就相當于被C++運行時庫接管,子線程執行完畢后,由運行時庫負責清理該線程相關的資源。使用detach之后,表明就失去了對子線程的控制。

void func()
{cout << "void func()" << endl;cout << "I'm child thread" << endl;
}void test()
{cout << "I'm main thread" << endl;thread th1(func);th1.join();//主線程等待子線程
}

線程的狀態

線程類中有一成員函數joinable,可以用來檢查線程的狀態。如果該函數為true,表示可以使用join()或者detach()函數來管理線程生命周期。

void test()
{thread t([]{cout << "Hello, world!" << endl;});if (t.joinable()) {t.detach();}
}void test2()
{thread th1([]{cout << "Hello, world!" << endl;});if (t.joinable()) {t.join();}
}

線程id

為了唯一標識每個線程,可以給每個線程一個id,類型為std::thread::id,可以使用成員函數get_id()進行獲取。

void test()
{thread th1([](){cout << "子線程ID:" << std::this_thread::get_id() << endl;});th1.join();
}

互斥鎖mutex

互斥鎖是一種同步原語,用于協調多個線程對共享資源的訪問。互斥鎖的作用是保證同一時刻只有一個線程可以訪問共享資源,其他線程需要等待互斥鎖釋放后才能訪問。在多線程編程中,多個線程可能同時訪問同一個共享資源,如果沒有互斥鎖的保護,就可能出現數據競爭等問題。

然而,互斥鎖的概念并不陌生,在Linux下,POSIX標準中也有互斥鎖的概念,這里我們說的互斥鎖是C++11語法層面提出來的概念,是C++語言自身的互斥鎖std::mutex,互斥鎖只有兩種狀態:上鎖與解鎖。

2、頭文件

#include <mutex>
class mutex;

3、常用函數接口

3.1、構造函數
constexpr mutex() noexcept;
mutex( const mutex& ) = delete;
3.2、上鎖
void lock();
3.3、嘗試上鎖
bool try_lock();
3.4、解鎖
void unlock();
3.5、使用示例
int gCount = 0;
mutex mtx;//初始化互斥鎖
?
void threadFunc()
{for(int idx = 0; idx < 1000000; ++idx){mtx.lock();//上鎖++gCount;mtx.unlock();//解鎖}
}
?
int main(int argc, char *argv[])
{thread th1(threadFunc);thread th2(threadFunc);
?th1.join();th2.join();cout << "gCount = " << gCount << endl;return 0;
}

三、lock_guard與unique_lock

在 C++ 多線程編程中,std::lock_guard?和?std::unique_lock?都是用于管理互斥鎖(std::mutex)的RAII 風格工具類,核心作用是自動加鎖和解鎖,避免手動操作鎖導致的死鎖(如忘記解鎖、異常時未釋放鎖等問題)。但它們的靈活性和適用場景有顯著區別。

一、核心共同點

  • 都遵循RAII 原則:構造時獲取鎖,析構時自動釋放鎖(無論正常退出還是異常退出)。
  • 都用于保護臨界區,防止多線程并發訪問共享資源導致的數據競爭。

二、關鍵區別與適用場景

特性std::lock_guardstd::unique_lock
靈活性簡單,功能有限靈活,支持更多操作
手動解鎖不支持(只能通過析構函數自動解鎖)支持(通過?unlock()?手動解鎖)
延遲鎖定不支持(構造時必須鎖定)支持(通過?std::defer_lock?延遲鎖定)
嘗試鎖定不支持支持(通過?std::try_to_lock?嘗試鎖定)
所有權轉移不支持(不可復制、不可移動)支持(可移動,不可復制)
性能開銷更低(輕量級)略高(因靈活性帶來的額外狀態管理)
適用場景簡單臨界區(全程需要鎖定)復雜場景(如條件變量、中途解鎖、延遲鎖定等)

三、詳細說明與示例

1.?std::lock_guard:簡單場景的首選

lock_guard?是輕量級鎖管理工具,設計用于最簡單的場景:進入臨界區時加鎖,離開時解鎖,全程不需要手動干預。

特點

  • 構造函數必須鎖定互斥量(要么直接鎖定,要么接受一個已鎖定的互斥量,通過?std::adopt_lock?標記)。
  • 沒有?unlock()?方法,只能在析構時自動解鎖(通常是離開作用域時)。
  • 不可復制、不可移動,所有權無法轉移。

示例

#include <mutex>
#include <iostream>std::mutex mtx;
int shared_data = 0;void increment() {// 構造時自動鎖定mtx,離開作用域(函數結束)時析構,自動解鎖std::lock_guard<std::mutex> lock(mtx);// 臨界區:安全訪問共享資源shared_data++;std::cout << "當前值: " << shared_data << std::endl;// 無需手動解鎖,lock析構時自動處理
}

適用場景

  • 臨界區邏輯簡單,從進入到退出全程需要鎖定。
  • 不需要中途解鎖、延遲鎖定等復雜操作。
  • 追求最小性能開銷。
2.?std::unique_lock:復雜場景的靈活選擇

unique_lock?是功能更全面的鎖管理工具,支持手動解鎖、延遲鎖定、嘗試鎖定等操作,適合需要靈活控制鎖狀態的場景。

特點

  • 支持延遲鎖定:通過?std::defer_lock?標記,構造時不鎖定互斥量,后續可通過?lock()?手動鎖定。
  • 支持手動解鎖:通過?unlock()?方法中途釋放鎖,之后可再次通過?lock()?重新鎖定。
  • 支持嘗試鎖定:通過?std::try_to_lock?標記,嘗試鎖定互斥量(成功返回 true,失敗不阻塞)。
  • 支持所有權轉移:可通過移動語義(std::move)轉移鎖的所有權(不可復制)。
  • 是條件變量(std::condition_variable)的必需參數:條件變量的?wait()?方法需要?unique_lock?作為參數,因為?wait()?會在等待時釋放鎖,被喚醒時重新獲取鎖(這要求鎖可以手動解鎖和鎖定)。

示例 1:延遲鎖定與手動解鎖

#include <mutex>
#include <iostream>std::mutex mtx;void complex_operation() {// 延遲鎖定:構造時不鎖定,僅關聯互斥量std::unique_lock<std::mutex> lock(mtx, std::defer_lock);// 做一些不需要鎖定的操作std::cout << "準備鎖定..." << std::endl;// 手動鎖定lock.lock();std::cout << "已鎖定,執行臨界區操作..." << std::endl;// 中途手動解鎖(釋放鎖,允許其他線程訪問)lock.unlock();std::cout << "臨時解鎖,執行其他操作..." << std::endl;// 再次鎖定lock.lock();std::cout << "再次鎖定,完成剩余操作..." << std::endl;// 析構時自動解鎖(若當前處于鎖定狀態)
}

示例 2:與條件變量配合

#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;void consumer() {std::unique_lock<std::mutex> lock(mtx);// 等待條件滿足:會釋放鎖并阻塞,被喚醒時重新獲取鎖cv.wait(lock, []{ return data_ready; });// 條件滿足,執行消費操作std::cout << "數據已準備好,開始處理..." << std::endl;
}void producer() {{std::lock_guard<std::mutex> lock(mtx);data_ready = true; // 生產數據} // 離開作用域,自動解鎖cv.notify_one(); // 通知消費者
}int main() {std::thread t1(consumer);std::thread t2(producer);t1.join();t2.join();return 0;
}

適用場景

  • 需要中途解鎖(如臨界區中間有耗時操作但無需鎖定)。
  • 需要延遲鎖定(如先做準備工作,再根據條件決定是否鎖定)。
  • 需要與條件變量配合(wait()?必須使用?unique_lock)。
  • 需要轉移鎖的所有權(如將鎖傳遞給其他函數)。

四、總結

  • 優先使用?std::lock_guard:當場景簡單,臨界區全程需要鎖定時,它更輕量、更高效。
  • 使用?std::unique_lock:當需要靈活性(手動解鎖、延遲鎖定、配合條件變量等)時,犧牲少量性能換取功能。

兩者的核心目標都是安全管理鎖的生命周期,避免手動操作鎖導致的錯誤,選擇時主要依據場景的復雜度和靈活性需求。

條件變量condition_variable

在 C++ 多線程編程中,std::condition_variable(條件變量)是用于線程間同步的核心機制,它允許線程在滿足特定條件前阻塞等待,當條件滿足時被其他線程喚醒,從而實現高效的協作(避免無效輪詢)。

一、核心作用

條件變量解決的核心問題:讓線程在 “條件不滿足” 時進入休眠狀態,在 “條件滿足” 時被喚醒繼續執行,避免線程通過 “輪詢”(反復檢查條件)浪費 CPU 資源。

例如:

  • 消費者線程等待生產者線程生成數據(“數據就緒” 是條件)。
  • 主線程等待子線程完成初始化(“初始化完成” 是條件)。

二、核心 API 與工作機制

std::condition_variable?定義在?<condition_variable>?頭文件中,核心方法如下:

方法作用
wait(lock, pred)阻塞當前線程,釋放鎖并等待被喚醒;被喚醒后重新獲取鎖,檢查pred是否為true,若為true則繼續執行,否則重新阻塞。
notify_one()喚醒一個正在等待該條件變量的線程(若有)。
notify_all()喚醒所有正在等待該條件變量的線程。
關鍵細節:
  1. 必須配合互斥鎖:條件變量的操作必須與互斥鎖(std::mutex)結合,且必須使用?std::unique_lock(而非?std::lock_guard),因為?wait()?過程需要先釋放鎖、被喚醒后重新獲取鎖unique_lock?支持手動解鎖 / 加鎖,lock_guard?不支持)。

  2. 處理 “虛假喚醒”:操作系統可能在無明確通知時喚醒線程(虛假喚醒),因此?wait()?必須配合條件謂詞(pred)?使用,確保只有當條件真正滿足時才繼續執行。

三、工作流程(以生產者 - 消費者為例)

  1. 消費者線程

    • 鎖定互斥鎖,檢查條件(如 “數據是否就緒”)。
    • 若條件不滿足,調用?wait():釋放鎖并阻塞等待。
    • 被喚醒后,重新獲取鎖,再次檢查條件(避免虛假喚醒)。
    • 條件滿足時,執行操作(如消費數據)。
  2. 生產者線程

    • 鎖定互斥鎖,修改共享資源(如生成數據)。
    • 調用?notify_one()?或?notify_all()?喚醒等待的消費者。
    • 釋放鎖(由?unique_lock?或?lock_guard?自動完成)。

四、完整示例:生產者 - 消費者模型

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>// 共享隊列(緩沖區)
std::queue<int> buffer;
const int MAX_SIZE = 5;  // 緩沖區最大容量// 同步工具
std::mutex mtx;
std::condition_variable cv_producer;  // 生產者等待的條件變量(緩沖區不滿)
std::condition_variable cv_consumer;  // 消費者等待的條件變量(緩沖區非空)// 生產者:向緩沖區添加數據
void producer(int id) {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(mtx);// 等待緩沖區不滿(若滿則阻塞)cv_producer.wait(lock, []{ return buffer.size() < MAX_SIZE; });// 生產數據int data = id * 100 + i;buffer.push(data);std::cout << "生產者 " << id << " 生產: " << data << ",緩沖區大小: " << buffer.size() << std::endl;// 通知消費者:緩沖區非空cv_consumer.notify_one();// 模擬生產耗時std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}// 消費者:從緩沖區取出數據
void consumer(int id) {for (int i = 0; i < 10; ++i) {std::unique_lock<std::mutex> lock(mtx);// 等待緩沖區非空(若空則阻塞)cv_consumer.wait(lock, []{ return !buffer.empty(); });// 消費數據int data = buffer.front();buffer.pop();std::cout << "消費者 " << id << " 消費: " << data << ",緩沖區大小: " << buffer.size() << std::endl;// 通知生產者:緩沖區不滿cv_producer.notify_one();// 模擬消費耗時std::this_thread::sleep_for(std::chrono::milliseconds(150));}
}int main() {// 創建2個生產者和2個消費者std::thread p1(producer, 1);std::thread p2(producer, 2);std::thread c1(consumer, 1);std::thread c2(consumer, 2);// 等待所有線程完成p1.join();p2.join();c1.join();c2.join();return 0;
}

五、關鍵注意事項

  1. 必須使用?unique_lockwait()?方法的第一個參數必須是?std::unique_lock<std::mutex>,因為?wait()?內部會執行 “解鎖→阻塞→被喚醒后重新加鎖” 的操作,unique_lock?支持這種靈活的鎖狀態管理(lock_guard?不支持手動解鎖,無法配合?wait())。

  2. 條件謂詞不可省略:即使你認為 “不會有虛假喚醒”,也必須在?wait()?中傳入條件謂詞(第二個參數)。例如:

    // 錯誤:未處理虛假喚醒
    cv.wait(lock); // 正確:確保條件滿足才繼續
    cv.wait(lock, []{ return condition; }); 
    
  3. notify_one()?與?notify_all()?的選擇

    • notify_one():喚醒一個等待線程,適用于 “只有一個線程能處理” 的場景(如緩沖區只有一個數據)。
    • notify_all():喚醒所有等待線程,適用于 “多個線程都能處理” 的場景(如廣播一個全局事件)。過度使用?notify_all()?可能導致線程喚醒后競爭鎖,浪費資源。
  4. 避免持有鎖時長時間操作:喚醒線程后,應盡快釋放鎖(完成臨界區操作),避免其他線程被喚醒后因無法獲取鎖而阻塞。

  5. 生命周期管理:確保條件變量在所有等待線程退出前保持有效,避免訪問已銷毀的條件變量。

六、總結

std::condition_variable?是多線程協作的高效工具,通過 “等待 - 通知” 機制替代輪詢,減少 CPU 浪費。其核心是:線程在條件不滿足時阻塞,條件滿足時被喚醒,配合互斥鎖和條件謂詞確保同步安全。典型應用包括生產者 - 消費者模型、線程池任務調度、事件驅動同步等。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98767.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98767.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98767.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL基礎全面解析

MySQL作為最流行的關系型數據庫管理系統之一&#xff0c;是每一位開發者必備的核心技能。本文將系統性地解析MySQL的基礎知識&#xff0c;結合關鍵概念與實戰應用&#xff0c;幫助您構建扎實的數據庫基礎。1. SQL與NoSQL的本質區別SQL&#xff08;結構化查詢語言&#xff09;數…

4、幽絡源微服務項目實戰:后端公共模塊創建與引入多租戶模塊

前言 上節我們將電網巡檢系統的前端vue2項目創建、配置&#xff0c;并構建了最基礎的多租戶界面&#xff0c;本節來繼續構建后端的公共模塊、多租戶模塊&#xff0c;并將公共模塊引入到多租戶模塊中。 創建公共模塊和多租戶模塊 在back父工程下創建兩個Module&#xff0c;和…

STM32學習路線開啟篇:芯片簡介與課程簡介

編寫不易,請多多指教,覺得不錯可以關注一下,相互學習 前言 一、課程配套資源 1、面包板 2、面包板專用的跳線 3、面包板的飛線 4、杜邦線 5、STM32F103C8T6最小系統板 6、0.96寸的OLED顯示屏模塊 7、電位器 8、按鈕 9、LED燈 10、STLINK 11、USB轉串口(TTL)模塊 12、源蜂鳴器模…

圖像直方圖

圖像直方圖就是用來統計圖像像素值分布的。灰度圖分布讀取灰度圖phone cv2.imread(phone.png, cv2.IMREAD_GRAYSCALE) a phone.ravel() plt.hist(a, bins256) plt.show()如何可以獲得當前像素值分布讀取各通道的像素值分布img cv2.imread(phone.png) colors (b, g, r) for …

分類別柱狀圖(Vue3)

效果圖&#xff1a;需求&#xff1a;男女年齡段占比<template><div class"go-ClassifyBar01"><v-chartref"vChartRef":option"option"style"width: 100%; height: 800px"></v-chart></div> </templa…

Apache Dubbo學習筆記-使用Dubbo發布、調用服務

Apache Dubbo經常作為一個RPC框架來使用&#xff0c;這篇文章主要介紹使用Dubbo配合注冊中心來發布和調用服務。 Apache Dubbo和Spring Boot、JDK的版本對應關系。 Dubbo 分支最新版本JDKSpring Boot組件版本詳細說明3.3.x (當前文檔)3.3.08, 17, 212.x、3.x詳情- 版本變更記錄…

Python學習——字典和文件

前面python的學習中我們已經學習了python的函數和列表元組相關的內容&#xff0c;接下來我們來學習剩下的python語法&#xff1a;字典和文件 相關代碼已經上傳到作者的個人gitee&#xff1a;樓田莉子/Python 學習喜歡請點個贊謝謝 目錄 字典 創建字典 查找key 新增/修改元素 …

swiper插件的使用

官方網址&#xff1a;https://www.swiper.com.cn/ 1、點擊導航欄&#xff0c;獲取Swiper里邊的下載Swiper 2、選擇要下載的版本【本次案例版本5.4.5】&#xff0c;然后解壓縮文件夾&#xff0c;拿到swiper.min.js和swiper.min.css文件&#xff0c;放到項目對應的css文件和js文…

Vue3+JS 組合式 API 實戰:從項目痛點到通用 Hook 封裝

Vue3 組合式 API 的實戰技巧 —— 組合式 API 幫我解決了不少 Options API 難以應對的問題&#xff0c;尤其是在代碼復用和復雜組件維護上。一、為什么放棄 Options API&#xff1f;聊聊三年項目里的真實痛點?剛接觸 Vue3 時&#xff0c;我曾因 “慣性” 繼續用 Options API 寫…

把 AI 塞進「電梯按鈕」——基于 64 kB 零樣本聲紋的離線故障預測按鈕

標簽&#xff1a;零樣本聲紋、電梯按鈕、離線 AI、TinyML、RISC-V、低功耗、GD32V303、故障預警 ---- 1. 背景&#xff1a;為什么按鈕要「聽聲音」&#xff1f; 全國 700 萬臺電梯&#xff0c;按鈕故障率 0.3 %/年&#xff0c;卻常出現&#xff1a; ? 機械卡滯、觸點氧化&…

清華大學聯合項目 論文解讀 | MoTo賦能雙臂機器人:實現零樣本移動操作

研究背景 移動操作是機器人領域的核心挑戰&#xff0c;它使機器人能夠在各種任務和動態日常環境中為人類提供幫助。傳統的移動操作方法由于缺乏大規模訓練&#xff0c;往往難以在不同任務和環境中實現泛化。而現有操作基礎模型雖在固定基座任務中表現出強泛化性&#xff0c;卻無…

go webrtc - 2 webrtc重要概念

webrtc是一套音視頻傳輸技術生態&#xff0c;不是一個協議或一個什么東西。3種模式本文基于 SFU 形式闡述&#xff01;重要概念&#xff1a;sfu 服務負責&#xff1a;信令 服務負責&#xff1a;peerConnection&#xff1a;track&#xff1a;房間&#xff1a;虛擬分組概念用戶&a…

“下游任務”概念詳解:從定義到應用場景

“下游任務”概念詳解&#xff1a;從定義到應用場景 一、什么是“下游任務”&#xff1f; 在機器學習&#xff08;尤其是深度學習&#xff09;中&#xff0c;“下游任務”&#xff08;Downstream Task&#xff09;是相對“上游過程”而言的目標任務——可以理解為&#xff1a;我…

視頻怎么做成 GIF?用 oCam 一鍵錄制 GIF 動畫超簡單

GIF 動圖因其生動直觀、無需點擊播放的特點&#xff0c;越來越受歡迎。你是否也曾看到一段有趣的視頻&#xff0c;想把它做成 GIF 發給朋友或用在PPT里&#xff1f;其實&#xff0c;將視頻片段轉換為 GIF 并不需要復雜的視頻剪輯技術&#xff0c;使用一款支持直接錄制為 GIF 的…

Vue.config.js中的Webpack配置、優化及多頁面應用開發

Vue.config.js中的Webpack配置、優化及多頁面應用開發 在Vue CLI 3項目中&#xff0c;vue.config.js文件是工程化配置的核心入口&#xff0c;它通過集成Webpack配置、優化策略和多頁面開發支持&#xff0c;為項目構建提供高度可定制化的解決方案。本文將從基礎配置、性能優化、…

行業學習【電商】:直播電商的去頭部化、矩陣號?

聲明&#xff1a;以下部分內容含AI生成這兩個詞是當前直播電商和MCN領域的核心戰略&#xff0c;理解了它們就理解了行業正在發生的深刻變化。一、如何理解“去頭部化”&#xff1f;“去頭部化” 指的是平臺或MCN機構有意識地減少對超頭部主播&#xff08;如曾經的李佳琦、薇婭&…

【MFC視圖和窗口基礎:文檔/視圖的“雙胞胎”魔法 + 單文檔程序】

大家好&#xff0c;我是你的MFC編程小伙伴&#xff01;學MFC就像探險古墓&#xff1a;到處是神秘的“房間”&#xff08;窗口&#xff09;和“寶藏”&#xff08;數據&#xff09;。今天咱們聊聊核心概念 – 視圖、窗口和文檔。這些是MFC的“骨架”&#xff0c;懂了它們&#x…

深度學習(六):代價函數的意義

在深度學習的浩瀚世界中&#xff0c;代價函數&#xff08;Cost Function&#xff09;&#xff0c;又稱損失函數&#xff08;Loss Function&#xff09;或目標函數&#xff08;Objective Function&#xff09;&#xff0c;扮演著至關重要的角色&#xff0c;它就像一個導航員&…

Kable使用指南:Android BLE開發的現代化解決方案

概述 Kable&#xff08;com.juul.kable:core&#xff09;是一個專為Android藍牙低功耗&#xff08;BLE&#xff09;開發設計的Kotlin協程友好庫。它通過提供簡潔的API和響應式編程模式&#xff0c;極大地簡化了BLE設備交互的復雜性。本文將詳細介紹Kable的使用方法&#xff0c;…

Android圖案解鎖繪制

使用到的庫是Pattern Locker,根據示例進行了修改,把默認樣式和自定義樣式進行了合并調整。 設置密碼 布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xm…