温馨提示×

Ubuntu中C++并发编程有哪些最佳实践

小樊
35
2026-01-03 18:20:07
栏目: 编程语言

在Ubuntu中进行C++并发编程时,遵循一些最佳实践可以帮助你编写更安全、更高效的代码。以下是一些推荐的实践:

1. 使用标准库的并发支持

C++11引入了标准库中的并发支持,包括<thread><mutex><condition_variable>等。尽量使用这些标准库组件来实现并发,因为它们提供了跨平台的解决方案,并且经过了广泛的测试。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void print_block(int n, char c) {
    std::lock_guard<std::mutex> guard(mtx);
    for (int i = 0; i < n; ++i) {
        std::cout << c;
    }
    std::cout << '\n';
}

int main() {
    std::thread th1(print_block, 50, '*');
    std::thread th2(print_block, 50, '$');

    th1.join();
    th2.join();

    return 0;
}

2. 避免共享状态

尽量减少线程间的共享状态,这样可以降低锁的使用频率,从而提高性能并减少死锁的风险。

3. 使用RAII管理锁

使用RAII(Resource Acquisition Is Initialization)技术来管理锁,例如std::lock_guardstd::unique_lock。这样可以确保锁在作用域结束时自动释放。

std::lock_guard<std::mutex> guard(mtx);
// 临界区代码

4. 避免死锁

确保在使用多个锁时遵循一致的锁定顺序,并且尽量减少锁的粒度。使用std::lock函数来同时锁定多个互斥量,以避免死锁。

std::mutex mtx1, mtx2;

void thread1() {
    std::lock(mtx1, mtx2);
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
    // 临界区代码
}

void thread2() {
    std::lock(mtx1, mtx2);
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
    // 临界区代码
}

5. 使用条件变量

使用std::condition_variable来实现线程间的同步,特别是在生产者-消费者问题中。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });
    std::cout << "Thread " << id << '\n';
}

void go() {
    std::lock_guard<std::mutex> lock(mtx);
    ready = true;
    cv.notify_all();
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) {
        threads[i] = std::thread(print_id, i);
    }
    std::this_thread::sleep_for(std::chrono::seconds(1));
    go();
    for (auto& th : threads) {
        th.join();
    }
    return 0;
}

6. 使用原子操作

对于简单的共享数据,可以使用std::atomic来实现无锁编程,这样可以提高性能。

#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 100000; ++i) {
        counter++;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << "Counter: " << counter << '\n';
    return 0;
}

7. 使用线程池

对于大量的短任务,使用线程池可以减少线程创建和销毁的开销。

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include <future>
#include <mutex>
#include <condition_variable>

class ThreadPool {
public:
    ThreadPool(size_t threads) : stop(false) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                for (;;) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            if (stop) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread& worker : workers) {
            worker.join();
        }
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};

int main() {
    ThreadPool pool(4);
    std::vector<std::future<int>> results;

    for (int i = 0; i < 8; ++i) {
        results.emplace_back(
            pool.enqueue([i] {
                std::cout << "hello "<< i << '\n';
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::cout << "world "<< i << '\n';
                return i * i;
            })
        );
    }

    for (auto&& result : results) {
        std::cout << result.get() << ' ';
    }
    std::cout << '\n';

    return 0;
}

8. 测试和调试

并发程序的测试和调试通常比较复杂。使用工具如gdbvalgrindhelgrind来检测竞态条件和死锁。此外,编写单元测试和集成测试来验证并发代码的正确性。

通过遵循这些最佳实践,你可以在Ubuntu中编写出更安全、更高效的C++并发程序。

0