在 C++ 中,线程安全的函数是指在多线程环境下可以安全调用,不会导致数据竞争或其他并发问题的函数。C++ 标准库提供了许多线程安全的函数,同时也要求开发者在使用自定义函数时确保线程安全。以下是一些常见的线程安全函数和实现线程安全的方法:
1. 标准库中的线程安全函数
C++ 标准库中的许多函数是线程安全的,尤其是那些不涉及共享状态的函数。
1.1 <iostream>
中的线程安全函数
std::cout
和std::cerr
:- 这些对象是线程安全的,但输出可能会交错。
- 如果需要保证输出顺序,可以使用互斥锁保护。
1.2 <atomic>
中的线程安全函数
std::atomic<T>
:- 所有操作(如
load()
、store()
、fetch_add()
等)都是原子的。 - 示例:
std::atomic<int> counter(0); counter++; // 线程安全
- 所有操作(如
1.3 <thread>
中的线程安全函数
std::thread::join()
:- 等待线程完成,线程安全的。
std::thread::detach()
:- 分离线程,线程安全的。
1.4 <mutex>
中的线程安全函数
std::mutex::lock()
和std::mutex::unlock()
:- 用于保护共享资源,线程安全的。
std::lock_guard
和std::unique_lock
:- 自动管理锁的生命周期,线程安全的。
1.5 <condition_variable>
中的线程安全函数
std::condition_variable::wait()
:- 等待条件变量,线程安全的。
std::condition_variable::notify_one()
和notify_all()
:- 通知等待的线程,线程安全的。
1.6 <future>
中的线程安全函数
std::future::get()
:- 获取异步操作的结果,线程安全的。
std::promise::set_value()
:- 设置异步操作的结果,线程安全的。
1.7 <chrono>
中的线程安全函数
std::chrono::system_clock::now()
:- 获取当前时间,线程安全的。
2. 自定义线程安全函数
如果需要编写自定义的线程安全函数,可以通过以下方式实现:
2.1 使用互斥锁(std::mutex
)
通过互斥锁保护共享资源,确保线程安全。
示例:
#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void threadSafeFunction() {std::lock_guard<std::mutex> lock(mtx); // 自动加锁和解锁shared_data++;std::cout << "Shared data: " << shared_data << std::endl;
}int main() {std::thread t1(threadSafeFunction);std::thread t2(threadSafeFunction);t1.join();t2.join();return 0;
}
2.2 使用原子操作(std::atomic
)
对于简单的数据类型,可以使用原子操作避免数据竞争。
示例:
#include <iostream>
#include <thread>
#include <atomic>std::atomic<int> shared_data(0);void threadSafeFunction() {shared_data++; // 原子操作std::cout << "Shared data: " << shared_data.load() << std::endl;
}int main() {std::thread t1(threadSafeFunction);std::thread t2(threadSafeFunction);t1.join();t2.join();return 0;
}
2.3 使用条件变量(std::condition_variable
)
通过条件变量实现线程间的同步。
示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;
bool ready = false;void threadSafeFunction() {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [] { return ready; }); // 等待条件变量std::cout << "Thread is running." << std::endl;
}int main() {std::thread t(threadSafeFunction);{std::lock_guard<std::mutex> lock(mtx);ready = true; // 修改条件}cv.notify_one(); // 通知等待的线程t.join();return 0;
}
3. 线程安全的容器
C++ 标准库提供了一些线程安全的容器,但需要注意的是,标准库容器本身并不是线程安全的,需要手动加锁保护。
3.1 线程安全的容器示例
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>std::mutex mtx;
std::vector<int> shared_vector;void threadSafeFunction(int value) {std::lock_guard<std::mutex> lock(mtx); // 加锁保护shared_vector.push_back(value);
}int main() {std::thread t1(threadSafeFunction, 1);std::thread t2(threadSafeFunction, 2);t1.join();t2.join();for (int v : shared_vector) {std::cout << v << " ";}return 0;
}
4. 线程安全的单例模式
单例模式是一种常见的设计模式,可以通过双重检查锁定(Double-Checked Locking)实现线程安全的单例。
示例:
#include <iostream>
#include <mutex>
#include <memory>class Singleton {
public:static Singleton& getInstance() {static std::once_flag flag;std::call_once(flag, [] { instance.reset(new Singleton); });return *instance;}private:Singleton() = default;static std::unique_ptr<Singleton> instance;
};std::unique_ptr<Singleton> Singleton::instance;int main() {Singleton& s1 = Singleton::getInstance();Singleton& s2 = Singleton::getInstance();std::cout << &s1 << " " << &s2 << std::endl; // 地址相同return 0;
}
总结
C++ 提供了丰富的工具来实现线程安全:
- 标准库函数:如
std::cout
、std::atomic
、std::mutex
等。 - 互斥锁:通过
std::mutex
保护共享资源。 - 原子操作:使用
std::atomic
避免数据竞争。 - 条件变量:通过
std::condition_variable
实现线程同步。 - 线程安全容器:手动加锁保护标准库容器。
- 单例模式:使用双重检查锁定或
std::call_once
实现线程安全的单例。
根据具体需求选择合适的方式,可以实现高效、安全的并发编程。