CppCon 2018 學習:What do you mean “thread-safe“

什么是“線程安全”?

線程安全”指的是一個函數、方法或代碼塊能夠在多個線程同時執行時,不會出現意外的交互或破壞共享數據,能夠安全地運行。
POSIX 對線程安全的定義很清楚:

“一個線程安全的函數可以在多個線程中被安全地并發調用,無論是與同一個函數的其他調用,還是與其他線程安全函數的調用。”

這意味著,當多個線程同時調用一個函數時,這個函數應該能夠正常工作,而不會導致數據破壞或未定義的行為。

線程安全的例子:

  1. 非線程安全的例子:
    int in[100], out[100];
    void thread1() {memcpy(&out, &in, sizeof(in));
    }
    void thread2() {memcpy(&out, &in, sizeof(in));
    }
    
    • 在這里,thread1thread2 都在并發訪問同一個 out 數組。
    • 如果這兩個線程同時調用 memcpy,就會發生 數據競爭。即一個線程在將數據復制到 out 時,另一個線程可能正在修改它,導致不可預測的結果。
    • 這個例子 不是線程安全的,因為 out 數組是共享的,并且沒有同步機制來防止多個線程同時訪問它。
  2. 線程安全的例子:
    void thread1() {int out[100];memcpy(&out, &in, sizeof(in));
    }
    void thread2() {int out[100];memcpy(&out, &in, sizeof(in));
    }
    
    • 在這種情況下,每個線程都使用自己獨立的 out 數組。
    • 由于每個線程都有自己的 out 數組,因此不會發生線程間的沖突。
    • 這個代碼 是線程安全的,因為每個線程都有自己的內存空間,不會互相干擾。

我們要“安全”避免什么?

在多線程的上下文中,提到“安全”主要是為了避免 競態條件(race condition)。

什么是 競態條件

競態條件是指當系統的行為依賴于事件的順序或時機,而這個順序或時機是無法控制的情況下發生的情況。在多線程程序中,當兩個或更多的線程同時訪問共享資源(如內存或變量),并且至少有一個線程修改了這個資源,就可能發生競態條件。

競態條件的例子:

  • 線程 A 和 線程 B 都訪問一個共享的計數器。
  • 線程 A 讀取計數器的值并將其加 1。
  • 線程 B 也讀取了相同的計數器值(在線程 A 寫入更新的值之前),然后將其加 1。
  • 最終的計數器值可能沒有反映出兩個線程的增量,因為兩個線程在更新之前都讀取了相同的初始值,導致 更新丟失錯誤的結果

為什么線程安全很重要?

線程安全確保了在多線程環境中,數據的一致性不會被破壞,并且避免了未定義的行為。如果沒有適當的線程同步,多個線程可能會相互干擾,導致 數據損壞不預期的行為,甚至 崩潰
總結:

  • 線程安全的代碼確保多個線程可以并發執行而不會發生不安全的交互。
  • 它通過在共享數據訪問上進行有效的同步管理,避免了競態條件。
  • 通常,使用 互斥鎖原子操作 等同步機制來保證代碼線程安全。
    如果你想要更深入的了解如何讓代碼線程安全,或者有任何問題,歡迎隨時提問!

我們要“安全”避免什么?—— 數據競爭(Data Races)

在多線程編程中,數據競爭(data race)是一個常見且嚴重的問題,它通常會導致不可預測的行為、錯誤的結果,甚至是程序崩潰。

什么是數據競爭?

根據 C++ 標準,一個程序中如果存在數據競爭,那么它包含了兩個潛在的并發沖突操作,其中至少有一個操作是 非原子操作
更具體地說:

數據競爭是指兩個表達式的評估存在沖突:

  • 其中一個表達式修改了某個內存位置
  • 另一個表達式在同一時間讀取或修改了同一個內存位置。

數據競爭的例子:

  1. 修改共享變量的例子:
    int i = 0;
    void thread1() {++i;  // 增加 i 的值
    }
    void thread2() {std::cout << i;  // 輸出 i 的值
    }
    
    • 問題:
      • thread1 在增量操作 i 時,i 的值可能會受到 thread2 讀取 i 時的影響。
      • 如果 thread1thread2 同時執行,i 的值可能會因為這兩者之間的沖突而變得不確定(例如,thread1 修改了 i,但 thread2 可能讀取了它的舊值,或者 i 的增量操作沒有被正確執行)。
    • 數據競爭的原因:
      • i 是共享資源,并且沒有同步機制(例如,鎖、原子操作等)來保護對 i 的并發訪問。
    • 解決方案:
      • 使用 原子操作(如 std::atomic)來確保對 i 的訪問是原子性的。
      • 或者使用互斥鎖(std::mutex)來同步對 i 的訪問。
  2. 修改字符串的例子:
    std::string s = "";
    void thread1() {s.append("foo");  // 向字符串中添加 "foo"
    }
    void thread2() {std::cout << s;  // 輸出字符串 s
    }
    
    • 問題:
      • thread1 修改了字符串 s,而 thread2 同時嘗試讀取 s
      • 如果這兩個線程同時執行,s 可能在 thread1 修改時發生沖突,導致 字符串內容不一致 或者 程序崩潰
    • 數據競爭的原因:
      • s 是共享資源,且沒有任何同步機制來防止線程間的并發訪問。
    • 解決方案:
      • 使用 std::mutex 來同步對 s 的訪問,確保只有一個線程可以修改或讀取 s
      • 或者考慮使用線程安全的數據結構(例如,std::atomic<std::string>,雖然這在 C++ 標準庫中并沒有直接支持)。

總結:

數據競爭是指兩個或多個線程在并發執行時,至少一個線程對共享數據的訪問是非原子的,且沒有適當的同步機制來確保數據一致性。數據競爭會導致不確定的行為和錯誤的結果,因此我們需要使用鎖、原子操作等同步機制來避免數據競爭。

  • 避免數據競爭的關鍵點:
    • 確保對共享資源的訪問是原子的。
    • 使用合適的同步機制(如 std::mutexstd::atomic)。
    • 盡量避免多個線程同時修改同一共享資源。
      通過這些措施,可以確保代碼在并發執行時的正確性和可靠性。

我們要“安全”避免什么?—— API 競爭(API Races)

除了 數據競爭,程序中的另一個常見并發問題是 API 競爭。API 競爭是指在同一個對象上執行并發操作時,該對象的 API 合同(或約定)并不允許這些操作并發執行。

什么是 API 競爭(API Race)?

API 競爭發生在一個程序執行兩個并發操作,這兩個操作在同一個對象上進行,但該對象的 API 并沒有保證這些操作是安全的,或者沒有提供適當的同步機制。
簡而言之,API 競爭是指多個線程或操作同時在同一個對象上執行時,違反了該對象的使用規則或約定,可能會導致不一致或錯誤的結果。

API 競爭的例子:

std::string s = "";
void thread1() {s.append("foo");  // 向字符串 s 中添加 "foo"
}
void thread2() {std::cout << s;   // 輸出字符串 s
}
問題分析:
  • 在這個例子中,s.append("foo")std::cout << s 是兩個操作,分別發生在不同的線程中。
  • 雖然 std::string 類本身是可以用于多個線程,但在并發訪問時,std::string 的 API 并沒有保證這些操作是線程安全的。
    • thread1 正在修改字符串 s
    • thread2 正在讀取字符串 s
  • 這些操作如果同時執行,可能會導致API 競爭,因為 std::string 沒有內建機制來同步并發訪問。這樣會導致以下問題:
    • 字符串的修改和讀取之間可能會發生沖突,導致讀取到不一致的值。
    • 在某些情況下,可能會發生內存損壞或者崩潰。
API 競爭的原因:
  • std::string 的 API 并沒有保證多線程環境下并發修改或讀取的安全性。
  • 沒有同步機制來保證 append<< 操作不會互相干擾。
解決方案:
  • 使用互斥鎖(mutex):使用 std::mutex 來保證在某個時刻,只有一個線程能夠訪問和修改 s
    std::mutex mtx;
    std::string s = "";
    void thread1() {std::lock_guard<std::mutex> lock(mtx);s.append("foo");
    }
    void thread2() {std::lock_guard<std::mutex> lock(mtx);std::cout << s;
    }
    
  • 使用線程安全的類或 API:如果需要多線程操作共享數據,選擇專門設計為線程安全的類,或者自己實現相應的同步機制。

總結:

API 競爭是指在并發執行時,程序操作的對象的 API 合同并沒有明確保障多個操作能夠安全地并發執行。為了避免 API 競爭,我們需要確保:

  • API 的約定:明確使用對象時,它的 API 是否支持并發操作,或者需要采取額外的同步措施。
  • 同步機制:通過互斥鎖、條件變量等同步工具,確保在多線程環境下共享資源的安全訪問。
    通過合理的同步機制和正確理解 API 合同,可以有效避免 API 競爭,提高程序的并發安全性。

識別 API 競爭(API Races)

API 競爭發生在多個線程同時操作同一個對象時,而該對象的 API 合同并不支持這種并發操作。識別 API 競爭是確保多線程程序安全的關鍵部分。下面我們來通過幾個例子詳細探討如何識別 API 競爭。

例 1: API 競爭示例 — 在同一對象上調用不同方法

Widget shared_widget;
void thread1() {// 線程1調用 shared_widget 的 foo() 方法shared_widget.foo();
}
void thread2() {// 線程2調用 shared_widget 的 bar() 方法shared_widget.bar();
}
分析:
  • 問題shared_widget 對象被多個線程同時操作,foo()bar() 方法可能對同一個數據進行修改。由于 Widget 類的 API 沒有保證這兩個方法的線程安全,這可能導致數據競爭(Data Race),比如兩個線程同時修改 shared_widget 內部的成員變量,從而導致數據不一致。
  • 解決方案
    • 確保 foo()bar() 方法是線程安全的。
    • 使用互斥鎖(std::mutex)保護對 shared_widget 對象的訪問,確保一次只有一個線程能夠調用這兩個方法。

例 2: API 競爭示例 — 通過不同的函數調用同一對象

Widget shared_widget;
void thread1() {Thingy t;t.foo(shared_widget);  // 線程1調用 t.foo(shared_widget)
}
void thread2() {Whatever w;w.bar(shared_widget);  // 線程2調用 w.bar(shared_widget)
}
分析:
  • 問題shared_widget 被兩個線程分別傳遞給 ThingyWhatever 類型的對象,并通過它們調用方法。這種情形依然可能導致 API 競爭,尤其是在 foo()bar() 方法內部執行對 shared_widget 的修改操作時。
  • 解決方案
    • Thingy::foo()Whatever::bar() 方法同步訪問 shared_widget,通過互斥鎖等同步機制確保同一時刻只有一個線程能訪問該對象。
    • 如果方法內部沒有修改 shared_widget,那么可以認為是線程兼容的,但仍需要確保沒有其他競爭條件。

例 3: 線程安全的設計 — 使用互斥鎖避免競爭條件

// 線程安全的 JobRunner 類
class JobRunner {JobSet running_;JobSet done_;std::mutex m_;  // 用于同步的互斥鎖void OnJobDone(Job* job) {m_.lock();running_.erase(job);  // 在互斥鎖保護下操作 running_ 集合done_.insert(job);    // 在互斥鎖保護下操作 done_ 集合m_.unlock();}
};
// 線程安全的 JobSet 類
class JobSet {std::set<Job*> jobs_;std::mutex m_;  // 用于同步的互斥鎖void erase(Job* job) {m_.lock();jobs_.erase(job);  // 線程安全地操作 jobs_ 集合m_.unlock();}
};
分析:
  • 問題解決:在這個設計中,JobRunnerJobSet 類通過使用 std::mutex 來同步對共享數據的訪問,確保了并發操作的安全性。這樣,多個線程可以安全地同時調用 OnJobDone()erase() 方法,而不會引起競爭條件。

例 4: 數據類型的線程安全

int shared_int;
void thread1() {Thingy t;t.foo(shared_int);  // 線程1調用 foo() 方法
}
void thread2() {Whatever w;w.bar(shared_int);  // 線程2調用 bar() 方法
}
void Thingy::foo(int i) {// 對 shared_int 進行修改
}
void Whatever::bar(const int& i) {// 讀取 shared_int
}
分析:
  • 問題:如果 Thingy::fooWhatever::bar 方法都對 shared_int 進行修改,或者至少是并發讀取,它們之間就可能會發生 API 競爭,尤其在 shared_int 沒有進行同步保護的情況下。
  • 解決方案
    • shared_int 進行同步保護,確保同一時刻只有一個線程能夠修改或讀取它。
    • 可以使用互斥鎖來保護對 shared_int 的訪問,或者使用 std::atomic 來確保并發操作時的原子性。

例 5: 線程兼容的對象

Widget shared_widget;
void thread1() {Thingy t;t.foo(shared_widget);
}
void Thingy::foo(const Widget& widget) {// 對 widget 進行只讀操作,不修改 shared_widget
}
void thread2() {Whatever w;w.bar(shared_widget);
}
void Whatever::bar(const Widget& widget) {// 對 widget 進行只讀操作,不修改 shared_widget
}
分析:
  • 問題:如果 shared_widget 是一個線程兼容的類型,并且在 foo()bar() 方法中僅進行讀取操作,而不進行修改,則不會發生 API 競爭。因為此時沒有線程同時修改 shared_widget,因此不會產生并發修改的問題。
  • 解決方案:確保在多線程環境下,只讀訪問共享對象,而不進行修改。這樣,即使多個線程同時訪問該對象,也不會引發競爭條件。

總結:

  1. API 競爭的核心問題:當多個線程在同一時刻訪問同一對象,而該對象的 API 合同不支持并發操作時,就會產生 API 競爭。
  2. 如何避免 API 競爭
    • 使用 互斥鎖std::mutex)來同步對共享對象的訪問。
    • 如果操作是只讀的,并且對象類型支持線程兼容(如 std::atomic),則可以避免競爭。
    • 設計線程安全的 API 或類型,確保在多線程環境下可以安全并發執行。
  3. 識別 API 競爭
    • 如果對象類型不是線程安全的,或者操作沒有同步機制,便會發生 API 競爭。
    • 如果對象的操作不符合 API 合同(例如不保證線程安全),則需要加鎖或其他同步措施。
      通過正確的同步和線程安全設計,可以有效避免 API 競爭,保證多線程程序的安全性和穩定性。

識別 API 競爭(API Races)

API 競爭指的是多個線程在同一時刻并發訪問同一對象的情況,導致不可預期的行為,尤其當該對象的 API 合同不支持并發時。正確識別并避免 API 競爭是確保多線程程序正確性和穩定性的關鍵。以下是通過幾個例子進一步理解 API 競爭及其避免方法。

例 1: 線程安全的 LazyStringView

class LazyStringView {const char* data_;mutable std::optional<size_t> size_;mutable std::mutex mu_;
public:size_t size() const {std::scoped_lock lock(mu_);  // 通過 scoped_lock 確保線程安全if (!size_) {size_ = strlen(data_);}return *size_;}
};
分析:
  • 問題LazyStringView 類的 size() 方法需要訪問 size_data_ 成員變量,這在多線程環境下是潛在的并發問題源。為了確保線程安全,在 size() 方法中使用了 std::mutex 來加鎖,確保每次只有一個線程能修改 size_
  • 解決方案:通過 std::scoped_lock 確保在訪問 size_data_ 時是互斥的。這樣可以確保即使多個線程同時調用 size(),也不會發生競爭條件。

例 2: 競爭條件示例 — 不同線程調用不同方法

Widget shared_widget;
void thread1() {Thingy t;t.foo(shared_widget);  // 線程1調用 foo(shared_widget)
}
void thread2() {Whatever w;w.bar(shared_widget);  // 線程2調用 bar(shared_widget)
}
分析:
  • 問題:在這個例子中,shared_widget 是一個共享對象,多個線程同時調用不同的方法可能導致競爭條件。假如 foo()bar() 方法都操作了 shared_widget,且這些方法沒有進行適當的同步,那么就會發生數據競爭。
  • 解決方案:可以使用互斥鎖(std::mutex)對對 shared_widget 的訪問進行保護,確保同一時刻只有一個線程能夠操作它。

例 3: 不同線程調用相同對象的不同方法

Widget shared_widget;
void thread1() {Thingy t;t.foo(shared_widget);
}
void Thingy::foo(const Widget& widget) {// 對 widget 的操作
}
void thread2() {Whatever w;w.bar(shared_widget);
}
void Whatever::bar(const Widget& widget) {// 對 widget 的操作
}
分析:
  • 問題shared_widget 可能在兩個不同線程中同時被傳遞給 foo()bar() 方法。如果這兩個方法都試圖訪問并修改 shared_widget,且沒有適當的同步機制,就會發生競爭條件。
  • 解決方案:確保 foo()bar() 方法在訪問 shared_widget 時是線程安全的。可以使用互斥鎖來同步訪問,或者將 Widget 設計為線程安全類型。

例 4: 線程不安全的函數調用

void Thingy::foo(const Widget&) {static int counter = 0;counter++;  // 修改靜態變量,可能導致數據競爭
}
void Whatever::bar(const Widget&) {static int counter = 0;counter++;  // 修改靜態變量,可能導致數據競爭
}
分析:
  • 問題foo()bar() 方法都在修改靜態變量 counter,如果兩個線程同時調用這兩個方法,且它們都試圖修改 counter,就會發生數據競爭。
  • 解決方案:將靜態變量 counter 設計為線程安全的,或者使用互斥鎖保護對 counter 的訪問,避免數據競爭。

線程安全與線程兼容類型

1. 線程安全的類型:
  • 如果對象的類型本身是線程安全的(例如使用 std::mutex 等機制進行同步),則該對象在多線程環境下是安全的,不能成為 API 競爭的源頭。
2. 線程兼容的類型:
  • 如果對象類型是線程兼容的(例如,只進行只讀操作,不會改變內部狀態),并且沒有被并發修改,那么該對象在并發訪問下是安全的。
3. 非線程安全類型:
  • 如果一個對象的類型在多線程環境下沒有設計為線程安全,且多個線程同時對它進行修改操作,那么就會發生 API 競爭。這種對象必須加以同步或避免并發訪問。

總結:

  • 線程安全的類型:如果一個對象的類型是線程安全的(如通過互斥鎖保護),它不能成為 API 競爭的源頭。
  • 線程兼容的類型:如果對象的狀態不會在并發訪問中被修改,那么即使多個線程同時訪問該對象,也不會發生 API 競爭。
  • 靜態變量與線程安全:靜態變量在多線程環境中需要特別注意,如果多個線程并發訪問并修改它們,可能會導致數據競爭。可以通過互斥鎖或其他同步機制來避免這個問題。
    通過合理的設計和同步機制,我們可以有效避免 API 競爭,確保多線程程序的安全性和正確性。

識別 API 競爭條件(API Races)

API 競爭(API Race)指的是在多線程環境中,多個線程并發訪問同一對象并試圖對其進行操作,導致不一致的結果或不可預測的行為。為避免這些問題,我們需要確保訪問共享數據時采取正確的同步機制。下面是一些典型的案例以及如何判斷和避免 API 競爭。

如何確保沒有 API 競爭條件

一行代碼如果要保證沒有 API 競爭條件,必須滿足以下條件:

  1. 不調用線程敵對函數(thread-hostile functions)。
  2. 所有輸入參數必須是活動的(live),即沒有被銷毀或處于非法狀態。
  3. 每個輸入對象必須滿足下列其中一個條件:
    • 不被其他線程訪問
    • 線程安全(thread-safe):即可以在多線程環境下安全使用。
    • 線程兼容(thread-compatible)且 不會被任何線程修改

示例 1: 數據競爭(Data Race)

代碼示例:
vector<int> shared_vec = {0, 0};
void thread1() {// 修改第一個元素++shared_vec[0];
}
void thread2() {// 修改第二個元素++shared_vec[1];
}
分析:
  • 問題:多個線程對同一 shared_vec 進行操作,雖然它們操作的是不同的元素([0][1]),但如果沒有合適的同步,可能會發生數據競爭。
  • 解決方案:對于不同的線程修改同一共享數據的情況,確保每個操作的同步性,或者確保每個線程訪問的數據是獨立的且不發生沖突。

示例 2: 線程不安全的 vector<bool>

代碼示例:
vector<bool> shared_vec = {false, false};
void thread1() {// 修改第一個元素shared_vec[0] = true;
}
void thread2() {// 修改第二個元素shared_vec[1] = true;
}
分析:
  • 問題vector<bool> 是一個特別的案例,它并沒有真正存儲 bool 類型,而是以壓縮的方式存儲,導致其并不是線程安全的。在多個線程并發寫入時會發生數據競爭。
  • 解決方案:避免使用 vector<bool>,或者確保線程間訪問時的同步。

示例 3: 對 vector<int> 的并發修改

代碼示例:
vector<int> shared_vec = {0, 0};
void thread1() {// 修改第一個元素++shared_vec[0];
}
void thread2() {// 修改第二個元素++shared_vec[1];
}
分析:
  • 問題:類似前面的例子,盡管 shared_vec[0]shared_vec[1] 是不同的元素,但它們仍然是 shared_vec 的成員,可能會發生數據競爭。
  • 解決方案:通過鎖(如 std::mutex)來同步對 shared_vec 的訪問,或者在訪問時確保不會發生沖突。

示例 4: 并發訪問 std::vector<int> 的部分范圍

代碼示例:
// 對迭代器區間內的每個元素進行加 1
template <typename Iterator>
void f(Iterator begin, Iterator end) {for (Iterator it = begin; it != end; ++it)++*it;
}
vector<int> v = {1, 2, 3};
void thread1() {f(v.begin(), v.begin() + 2);  // 修改前兩個元素
}
void thread2() {f(v.begin() + 1, v.end());  // 修改后兩個元素
}
分析:
  • 問題:這段代碼在兩個線程中并發執行,thread1thread2 都試圖修改 v 中的不同部分([0, 1][1, 2])。這意味著兩者在操作相同的元素時會發生數據競爭。
  • 解決方案:確保在訪問共享資源時加鎖,或者重構代碼避免同時操作共享數據。

示例 5: 線程不安全的成員函數

代碼示例:
class Widget {int* counter_;
public:Widget(int* counter) : counter_(counter) {}// 線程不安全!void Twiddle() {++*counter_;}
};
Widget MakeWidget();
void thread1() {Widget w = MakeWidget();w.Twiddle();
}
void thread2() {Widget w = MakeWidget();w.Twiddle();
}
分析:
  • 問題:多個線程可能同時調用 Twiddle(),而 counter_ 是一個指向共享數據的指針。如果沒有同步機制,可能會導致數據競爭。
  • 解決方案:使用鎖來保護 counter_ 的訪問,確保每次只有一個線程可以修改共享的 counter_

推薦的最佳實踐

對于庫代碼:
  1. 使類型線程兼容(thread-compatible):盡量設計為線程兼容,如果有必要,再設計為線程安全(thread-safe)。
  2. 明確文檔化:清晰地文檔化哪些類型是線程安全的,哪些是線程不兼容的。推薦將其他類型顯式標注為線程兼容(thread-compatible)。
  3. 避免暴露內部狀態:在設計中小心地暴露子對象,尤其是不可變的數據。
  4. 避免使用線程不安全的函數:如避免隱式共享的可變狀態,避免使用指向共享數據的私有指針。
對于應用程序代碼:
  1. 使共享對象線程安全:或者確保它們是線程兼容且不可變的。不要讓它們成為并發操作的源頭。

總結

  • 線程安全:在多線程環境中,不會因并發操作導致數據損壞或未定義行為。使用鎖、原子操作等技術來確保線程安全。
  • 線程兼容:即對象可以在多個線程中并發訪問,但不會被修改。如果沒有修改操作,可以認為是線程兼容的。
  • API 競爭:多個線程對同一對象的不同操作可能會導致不一致的結果。通過合理設計數據訪問和同步機制,可以避免這種情況。
    總之,確保多線程程序的正確性需要考慮對象的訪問權限、線程安全性以及同步機制,尤其是避免并發訪問導致的 API 競爭條件。

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

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

相關文章

熱方程初邊值問題解法

已知公式&#xff1a; u ( x , t ) ∫ ? ∞ ∞ G ( x , y , t ) g ( y ) d y . u(x,t)\int_{-\infty}^{\infty}G(x,y,t)g(y)dy. u(x,t)∫?∞∞?G(x,y,t)g(y)dy. &#xff08;1&#xff09; 其中 G ( x , y , t ) 1 2 k π t e ? ( x ? y ) 2 4 k t G(x,y,t)\frac{1}{2…

怎樣理解:source ~/.bash_profile

場景復現 $ source ~/.bash_profileAnalysis 分析 一句話概括 source ~/.bash_profile “在 當前 終端會話里&#xff0c;立刻執行并加載 ~/.bash_profile 中的所有命令&#xff0c;讓其中定義的環境變量、函數、alias 等即時生效&#xff0c;而無需重新登錄或開新 Shell。…

搜索問答技術概述:基于知識圖譜與MRC的創新應用

目錄 一、問答系統應用分析 二、搜索問答技術與系統 &#xff08;一&#xff09;需求和信息分析 問答需求類型 多樣的數據源 文本組織形態 &#xff08;二&#xff09;主要問答技術介紹 發展和成熟度分析 重點問答技術基礎&#xff1a;KBQA和DeepQA KBQA&#xff08;…

TCP數據的發送和接收

本篇文章結合實驗對 TCP 數據傳輸中的重傳機制、滑動窗口以及擁塞控制做簡要的分析學習。 重傳 實驗環境 這里使用兩臺騰訊云服務器&#xff1a;vm-1&#xff08;172.19.0.3&#xff09;和vm-2&#xff08;172.19.0.6&#xff09;。 超時重傳 首先 vm-1 作為服務端啟動 nc…

python 保存二維數組到本地

Python中保存二維數組有多種方法&#xff0c;以下是常用的幾種方式&#xff1a;1. 使用NumPy&#xff08;推薦&#xff09;import numpy as np# 創建二維數組 arr np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])# 保存為.npy文件&#xff08;NumPy專用格式&#xff09; np.save…

LIN總線通訊中從節點波特率同步原理

波特率同步原理&#xff1a;從節點如何通過0x55校準時鐘&#xff1f; 一、同步場的核心作用&#xff1a;統一“時間標尺” 在LIN總線中&#xff0c;主節點與從節點各自擁有獨立的時鐘源&#xff08;如MCU內部RC振蕩器&#xff09;&#xff0c;但由于制造工藝差異&#xff0c;…

【Unity筆記02】訂閱事件-自動開門

流程 當玩家移動到觸發區域的時候&#xff0c;門自動打開 事件系統 using System; using System.Collections; using System.Collections.Generic; using UnityEngine;public class EventSystem : MonoBehaviour {public static EventSystem Instance { get; private set; }…

控制臺字符動畫

旋轉的立方體 #include <cstdint> #include <cstdio> #include <iostream> #include <cstring> #include <cmath> #include <cstdlib> #include <ctime> #include <thread> using namespace std;float angleX .0f; float a…

基于 PyTorch 的貓狗圖像分類實戰

基于 PyTorch 的貓狗圖像分類實戰 項目背景簡介 深度學習框架 PyTorch 因其動態計算圖和靈活易用性&#xff0c;被廣泛應用于圖像分類等計算機視覺任務。在入門計算機視覺領域時&#xff0c;常常以手寫數字識別&#xff08;MNIST&#xff09;作為 “Hello World”&#xff0c…

SwiftUI 7(iOS 26 / iPadOS 26)中玻璃化標簽頁的全新玩法

&#x1f378; Liquid Glass 登場&#xff1a;界面設計煥然一新 WWDC25 可謂驚喜連連&#xff0c;其中最引人矚目的變革之一&#xff0c;莫過于蘋果推出的全新跨平臺設計語言 —— Liquid Glass&#xff08;液態玻璃&#xff09;。這一設計風格涵蓋了從按鈕到導航欄&#xff0…

PDF處理控件Spire.PDF教程:在Java中讀取PDF,提取文本、圖片和表格

在數據驅動的現代開發中&#xff0c;高效處理 PDF 文檔已成為 Java 開發者不可或缺的核心能力。無論是處理各類發票掃描件、業務分析報告&#xff0c;還是包含豐富圖表的技術文檔&#xff0c;掌握 Java 版的 PDF 解析技術都將大幅提升數據處理效率&#xff0c;充分釋放文檔中的…

跨平臺游戲引擎 Axmol-2.7.0 發布

Axmol 2.7.0 版本是一個以錯誤修復和功能改進為主的次要LTS長期支持版本 &#x1f64f;感謝所有貢獻者及財務贊助者&#xff1a;scorewarrior、peterkharitonov、duong、thienphuoc、bingsoo、asnagni、paulocoutinhox 重大變更 Android Studio 最低版本要求升級至 2025.1.1…

XML 筆記

<image src"hue.gif" width"100" height"auto" align"left"/> <br/> 換行 在 XML 中&#xff0c;<![CDATA[ 和 ]]> 用于定義一個 CDATA 節&#xff08;Character Data Section&#xff09;。CDATA 節是用于將一段…

Python實現優雅的目錄結構打印工具

Python實現優雅的目錄結構打印工具 在軟件開發、系統管理和日常工作中&#xff0c;我們經常需要查看和分析目錄結構。 工具功能概述 這個DirectoryPrinter類提供了以下功能&#xff1a; 遞歸打印目錄結構可配置是否顯示隱藏文件可設置最大遞歸深度自定義縮進和文件/文件夾符…

【Python】文件打開:with open具體解析

示例 # 使用 with 語句打開文件并讀取內容 with open(pi.txt, r) as file_object:contents file_object.read()print(contents) # 文件在代碼塊結束后自動關閉with 解析 with 是 Python 中的上下文管理器語法&#xff0c;用于確保某個操作完成后自動執行清理操作。它常用于文…

Acrel-1000系列分布式光伏監控系統在湖北荊門一馬光彩大市場屋頂光伏發電項目中應用

摘 要&#xff1a;分布式光伏發電能夠對日益嚴重的環境壓力起到有效緩解作用,在當前對環境保護需求越來越大情況下,發電行業在發展中不但要提升發電效率,同時也需要降低成本。分布式光伏發電主要是利用風能和太陽能等可再生清潔能源進行發電,對于空氣質量具有改善效果,和傳統發…

CentOS-6與CentOS-7的網絡配置IP設置方式對比 筆記250706

CentOS-6與CentOS-7的網絡配置IP設置方式對比 筆記250706 1?? 參考 1 CentOS-6 與 CentOS-7 的網絡配置IP設置方式對比 CentOS 6 和 CentOS 7 在網絡配置上存在顯著差異&#xff0c;主要體現在配置文件結構、管理工具、服務機制和命令集等方面。以下是兩者的核心對比&#x…

【網絡系列】HTTP 429 狀態碼

博客目錄 HTTP 429 狀態碼的定義與背景產生 429 錯誤的常見場景1. API 速率限制觸發2. 網絡爬蟲行為被檢測3. 分布式拒絕服務(DDoS)防護4. 用戶/IP 特定限流策略5. 應用程序邏輯錯誤 深入解讀 429 響應的關鍵頭部信息Retry-After 頭部X-RateLimit 系列頭部RateLimit 標準化頭部…

C++無鎖數據結構:CAS(Compare-and-Swap)

在高并發場景下&#xff0c;傳統鎖機制帶來的線程阻塞和上下文切換開銷成為性能瓶頸。無鎖數據結構通過原子操作實現線程安全&#xff0c;避免了鎖的使用&#xff0c;成為高性能系統的關鍵技術。本文將深入探討C中基于CAS&#xff08;Compare-and-Swap&#xff09;的無鎖數據結…

【數字圖像處理】

數字圖像處理 緒論1. 數字圖像處理基本概念2. 數字圖像處理系統的組成3. 數字圖像處理技術研究的內容4. 數字圖像處理技術的應用領域5. 圖像處理技術涉及的學科領域 圖像處理基礎1. 電磁波譜與可見光譜2. 人眼的亮度視覺特性3. 圖像的表示4. 空間分辨率和灰度級分辨率5. 像素間…