【编程】C++语言编程规范-2

编程实践

结合C++ Effective系列参考树、尤其是工程经验教训的总结。

并发

  • 除非必要,尽量少用线程。
  • 多线程编程要守护好内存,使用atomic、mutex、condition variable、future、semaphore、latch、barrier等同步机制避免数据竞争。
  • 尽量缩小临界区,临界区指独占的资源,禁止其他线程访问变量的代码片段,如持有mutex的作用域。与回调函数相似,应尽可能精简此类操作,避免执行耗时的处理、阻塞性操作如sleep。能在临界区、回调函数外处理的,尽可能在外部。
  • 必须正确管理多线程中的对象,避免某线程正在访问的对象被另一线程清理,如用move方法使对象从一个线程正确移交给另一个线程,避免内存泄漏。
  • 使用condition variable时,必须增加条件判断并在循环中等待。下例中,线程ReceivingUnPro中条件变量错过了通知,缺少条件判断,就始终处于等待状态,而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;
}

理论上,条件变量有虚假唤醒问题,所以要条件判断避免。

  • 用std::lock_gaurd、std::unique_lock确保锁被释放,不要用std::mutex的lock()、unlock()以免遗忘或异常导致dead lock。
    错误的例子,
#include <mutex>
std::mutex x;
{
   
    x.lock();
    // 处理数据的代码
    // 发生异常,导致后面未执行
    x.unlock(); 
}

下例中出现dead lock,

#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;
}

对象与内存管理

  • 避免访问越界,如索引数组前判断下标是否超出数组区间。
  • 申请内存要先检查大小。
  • 数组作为函数参数,若是已知的固定长度,建议用std::array;若不确定长度,传递来的数组表现为指针,则函数参数要加上数组长度,或者将数组的指针与长度封装为一个类。
  • 避免函数返回其局部变量的地址,建议改为返回复制的值。
  • lambda的作用范围超出局部时,如用于线程等,按引用捕获可能导致局部变量过早被清理,应改为按值捕获;应明确捕获的变量、合理的捕获类型。

常量

  • 建议优先用constexpr定义常量,编译时硬编码变量,提升效率;改关键字要求被修饰对象在编译时可确定变量的值。
  • 使用const修饰函数内部不会修改的参数,类内不变的变量、不修改不可变的成员变量的成员方法。
  • const实例化的对象,只允许调用其const方法。

相关推荐

  1. 编程C++语言编程规范-2

    2024-02-21 08:00:05       53 阅读
  2. 华为C&C++语言编程规范--笔记

    2024-02-21 08:00:05       37 阅读
  3. C语言代码编码规范

    2024-02-21 08:00:05       62 阅读
  4. C/C++语言的安全编码规范

    2024-02-21 08:00:05       52 阅读
  5. C++】编程规范之函数规则

    2024-02-21 08:00:05       39 阅读
  6. C++】编程规范之内存规则

    2024-02-21 08:00:05       34 阅读
  7. C语言 编程

    2024-02-21 08:00:05       57 阅读
  8. C#编程语言简介

    2024-02-21 08:00:05       66 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-21 08:00:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-21 08:00:05       100 阅读
  3. 在Django里面运行非项目文件

    2024-02-21 08:00:05       82 阅读
  4. Python语言-面向对象

    2024-02-21 08:00:05       91 阅读

热门阅读

  1. Android Studio 的六种基本布局

    2024-02-21 08:00:05       53 阅读
  2. py2neo和neo4j

    2024-02-21 08:00:05       51 阅读
  3. 代码随想录 栈与队列

    2024-02-21 08:00:05       49 阅读
  4. 服务器和电脑的区别是什么

    2024-02-21 08:00:05       60 阅读
  5. Android的消息机制--Handler

    2024-02-21 08:00:05       50 阅读
  6. django的DRF(三)

    2024-02-21 08:00:05       34 阅读
  7. effective c++ 笔记 条款26-31

    2024-02-21 08:00:05       42 阅读
  8. 九、计算机视觉-形态学基础概念

    2024-02-21 08:00:05       44 阅读
  9. 计算机视觉发展的方向和潜在机会

    2024-02-21 08:00:05       48 阅读