編程實(shí)踐
結(jié)合C++ Effective系列參考樹、尤其是工程經(jīng)驗(yàn)教訓(xùn)的總結(jié)。
并發(fā)
- 除非必要,盡量少用線程。
- 多線程編程要守護(hù)好內(nèi)存,使用atomic、mutex、condition variable、future、semaphore、latch、barrier等同步機(jī)制避免數(shù)據(jù)競爭。
- 盡量縮小臨界區(qū),臨界區(qū)指獨(dú)占的資源,禁止其他線程訪問變量的代碼片段,如持有mutex的作用域。與回調(diào)函數(shù)相似,應(yīng)盡可能精簡此類操作,避免執(zhí)行耗時(shí)的處理、阻塞性操作如sleep。能在臨界區(qū)、回調(diào)函數(shù)外處理的,盡可能在外部。
- 必須正確管理多線程中的對(duì)象,避免某線程正在訪問的對(duì)象被另一線程清理,如用move方法使對(duì)象從一個(gè)線程正確移交給另一個(gè)線程,避免內(nèi)存泄漏。
- 使用condition variable時(shí),必須增加條件判斷并在循環(huán)中等待。下例中,線程
ReceivingUnPro
中條件變量錯(cuò)過了通知,缺少條件判斷,就始終處于等待狀態(tài),而ReceivingPro
依靠while判斷條件,解決了問題。
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
std::condition_variable g_dataCond;
std::string g_words = "";
std::mutex g_mtx;
bool g_isReady = false;
void ReceivingUnpro() {
std::unique_lock<std::mutex> lg(g_mtx);
std::cout << "waiting!" << std::endl;
g_dataCond.wait(lg);
std::cout << "Bad receiving thread got the message: " << g_words
<< std::endl;
}
void ReceivingPro() {
std::unique_lock<std::mutex> lg(g_mtx);
while (!g_isReady) {
g_dataCond.wait(lg);
}
std::cout << "Protected receiving thread got the message: " << g_words
<< std::endl;
}
void Sending() {
std::lock_guard<std::mutex> lg(g_mtx);
g_words.append("Go forward!");
g_isReady = true;
g_dataCond.notify_all();
}
int main() {
std::thread b(Sending);
b.join();
std::thread ap(ReceivingPro);
ap.join();
std::thread a(ReceivingUnpro);
a.join();
return 0;
}
理論上,條件變量有虛假喚醒問題,所以要條件判斷避免。文章來源:http://www.zghlxwxcb.cn/news/detail-833455.html
- 用std::lock_gaurd、std::unique_lock確保鎖被釋放,不要用std::mutex的lock()、unlock()以免遺忘或異常導(dǎo)致dead lock。
錯(cuò)誤的例子,
#include <mutex>
std::mutex x;
{
x.lock();
// 處理數(shù)據(jù)的代碼
// 發(fā)生異常,導(dǎo)致后面未執(zhí)行
x.unlock();
}
下例中出現(xiàn)dead lock,文章來源地址http://www.zghlxwxcb.cn/news/detail-833455.html
#include <unistd.h>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using std::cout;
using std::endl;
using std::lock_guard;
using std::mutex;
using std::chrono::seconds;
mutex m1, m2, m3;
std::condition_variable cnd;
std::mutex dt_mtx;
bool ready = false;
std::vector<int> vec_i;
void ProcessData() {
for (const auto i : vec_i) {
std::cout << i << " ";
}
std::cout << std::endl;
}
void T1F() {
std::unique_lock<std::mutex> lock(dt_mtx);
cnd.wait(lock, [] { return ready; });
ProcessData();
}
void T2F() {
{
std::lock_guard<std::mutex> lock(dt_mtx);
for (int i = 0; i < 10; i++) {
vec_i.push_back(i);
sleep(1);
}
ready = true;
}
cnd.notify_all();
}
void Fun1() {
lock_guard<mutex> l1(m1);
std::this_thread::sleep_for(seconds(1));
lock_guard<mutex> l2(m2);
std::this_thread::sleep_for(seconds(1));
cout << "Fun1 is finishing" << endl;
}
void Fun2() {
lock_guard<mutex> l1(m2);
std::this_thread::sleep_for(seconds(1));
lock_guard<mutex> l2(m3);
std::this_thread::sleep_for(seconds(1));
cout << "Fun2 is finishing" << endl;
}
void Fun3() {
lock_guard<mutex> l1(m3);
std::this_thread::sleep_for(seconds(1));
lock_guard<mutex> l2(m1);
std::this_thread::sleep_for(seconds(1));
cout << "Fun3 is finishing" << endl;
}
int main() {
std::thread t1(Fun1);
std::thread t2(Fun2);
std::thread t3(Fun3);
if (t1.joinable()) {
cout << "t1 is joining" << endl;
t1.join();
}
if (t2.joinable()) {
cout << "t2 is joining" << endl;
t2.join();
}
if (t3.joinable()) {
cout << "t3 is joining" << endl;
t3.join();
}
#ifdef VERSION1
std::thread t1(T1F);
std::thread t2(T2F);
t1.join();
t2.join();
#endif
return 0;
}
對(duì)象與內(nèi)存管理
- 避免訪問越界,如索引數(shù)組前判斷下標(biāo)是否超出數(shù)組區(qū)間。
- 申請(qǐng)內(nèi)存要先檢查大小。
- 數(shù)組作為函數(shù)參數(shù),若是已知的固定長度,建議用std::array;若不確定長度,傳遞來的數(shù)組表現(xiàn)為指針,則函數(shù)參數(shù)要加上數(shù)組長度,或者將數(shù)組的指針與長度封裝為一個(gè)類。
- 避免函數(shù)返回其局部變量的地址,建議改為返回復(fù)制的值。
- lambda的作用范圍超出局部時(shí),如用于線程等,按引用捕獲可能導(dǎo)致局部變量過早被清理,應(yīng)改為按值捕獲;應(yīng)明確捕獲的變量、合理的捕獲類型。
常量
- 建議優(yōu)先用
constexpr
定義常量,編譯時(shí)硬編碼變量,提升效率;改關(guān)鍵字要求被修飾對(duì)象在編譯時(shí)可確定變量的值。 - 使用
const
修飾函數(shù)內(nèi)部不會(huì)修改的參數(shù),類內(nèi)不變的變量、不修改不可變的成員變量的成員方法。 - const實(shí)例化的對(duì)象,只允許調(diào)用其const方法。
到了這里,關(guān)于【編程】C++語言編程規(guī)范-2的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!