陕西住房建设部网站,wordpress 转载,手机版龙岩kk网,网站管理助手创建数据库概述 随着计算机硬件的发展#xff0c;多核处理器已经成为主流#xff0c;对程序并发执行能力的需求日益增长。C 11标准引入了一套全面且强大的并发编程支持库#xff0c;为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架#xff0c;极大地简化了多线程…概述 随着计算机硬件的发展多核处理器已经成为主流对程序并发执行能力的需求日益增长。C 11标准引入了一套全面且强大的并发编程支持库为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架极大地简化了多线程开发。 std::thread 在C 11中std::thread是用于创建和管理线程的核心组件。使用线程的一些要点如下。 1、创建线程。 通过调用std::thread构造函数传入要在线程中执行的函数或可调用对象以及任何必要的参数来创建线程。 2、线程函数。 可以是一个全局函数、类成员函数此时需要传递指向该类实例的指针或引用或者是一个满足Callable要求的类型。 3、线程执行。 一旦创建了std::thread对象线程就会尝试启动执行但具体何时开始执行由操作系统调度决定。 4、线程同步。 若多个线程共享数据通常需要使用互斥锁std::mutex、条件变量std::condition_variable或其他同步机制来避免竞态条件和数据不一致问题。 5、线程生命周期管理。 join()方法会阻塞当前线程直到被调用join()的线程完成其任务。detach()方法将线程从std::thread对象中分离使其成为一个守护线程当主线程退出而未调用join()时这个分离的线程仍然可以继续运行。但是如果分离的线程最后仍在运行且没有其他引用则可能会导致资源泄漏。 std::thread的具体使用可参考下面的示例代码。
#include iostream
#include thread
using namespace std;void ThreadFunc()
{cout Sub thread endl;
}int main() {thread myThread(ThreadFunc);cout Main thread init endl;myThread.join();cout Main thread exit endl;return 0;
} std::mutex std::mutex是用于实现线程间同步的基础工具它确保同一时间内只有一个线程能够访问被保护的资源或执行一段代码。为了简化锁的管理并防止死锁通常建议使用std::lock_guard或std::unique_lock这样的RAIIResource Acquisition Is Initialization机制来自动管理锁的生命周期。同时在复杂的情况下还可以结合条件变量等工具来实现更为灵活的线程同步逻辑。 在mutex头文件中定义了std::mutex类型它代表一个可重入互斥量。当多个线程试图同时获取已锁定的互斥量时除了已经持有该互斥量的线程外其他线程会被阻塞直到互斥量被解锁。std::mutex的主要成员函数如下。 1、构造函数默认构造一个未锁定的互斥量。
std::mutex mtx; 2、lock()将互斥量锁定如果互斥量已经被另一个线程锁定则调用此方法的线程将被阻塞直至互斥量变为可用。
mtx.lock(); 3、unlock()解锁互斥量允许等待的线程如果有获得所有权并继续执行。
mtx.unlock(); 4、try_lock()尝试锁定互斥量但不会阻塞。如果成功获取锁则返回true否则即互斥量已被锁定立即返回false。
if (mtx.try_lock())
{// 已经获取到锁
}
else
{// 未能获取到锁
} 5、RAII包装器类。为了确保即使在异常情况下也能正确释放互斥锁C 11提供了几个基于RAIIResource Acquisition Is Initialization原则的包装器类比如std::lock_guard和std::unique_lock。 std::lock_guard当lock_guard对象创建时自动锁定互斥量并在其析构时自动解锁互斥量从而避免忘记解锁导致的死锁问题。
std::lock_guardstd::mutex lock(mtx);
// 这里是受保护的代码区域
// lock析构时自动解锁 std::unique_lock提供了比lock_guard更多的灵活性比如手动锁定、解锁以及尝试锁定等。 std::condition_variable condition_variable是C标准库中的一个同步原语它是多线程编程中的一种关键工具主要用于线程间的通信和同步。它与互斥量mutex配合使用可以实现线程的等待和通知机制。具体来说std::condition_variable类提供了以下的功能。 1、等待。 当某个条件不满足时线程可以调用wait()函数释放互斥锁并进入等待状态直到其他线程对同一个条件变量调用notify_one()或notify_all()函数唤醒它。 2、唤醒。 notify_one()唤醒一个正在等待此条件变量的线程如果有多个线程在等待则唤醒其中一个。 notify_all()唤醒所有正在等待此条件变量的线程。 通常condition_variable的典型使用场景包括生产者/消费者模式、 barrier同步等通过它可以有效地控制线程在满足特定条件时才继续执行从而避免无效的循环检查或者竞争条件等问题。
#include iostream
#include thread
#include mutex
#include condition_variable
using namespace std;static mutex s_mutex;
static condition_variable s_condition;
static bool s_bReady false;void PrintID(int nID)
{unique_lockmutex lock(s_mutex);while (!s_bReady){// 当ready为false时线程会一直等待s_condition.wait(lock);}// 当其他线程修改ready为true并调用cv.notify_all()后这里会被唤醒cout Thread nID is running. endl;
}void Process()
{unique_lockmutex lock(s_mutex);s_bReady true;// 唤醒所有等待的线程s_condition.notify_all();
}int main()
{thread pThreads[10];for (int i 0; i 10; i){pThreads[i] thread(PrintID, i 1);}// 创建一个线程来修改ready并唤醒其他线程thread threadOther(ref(Process));for (auto t : pThreads){t.join();}threadOther.join();return 0;
} 在上面的示例代码中我们创建了10个线程它们都在等待一个条件s_bReady变为true。当Process线程将s_bReady设为true并调用s_condition.notify_all()后所有等待的线程都会被唤醒并打印自己的ID。 std::atomic std::atomic是C 11引入的标准库中的一个模板类它提供了一种能够在多线程环境中进行原子操作的类型安全方式。原子操作意味着即使在没有互斥量或其他同步机制的情况下该操作也能够从多个线程中以不可分割的方式执行即不会出现半个操作的现象确保了数据一致性。 使用std::atomic可以有效地处理简单的同步需求比如无锁计数器、标志位等并且相比传统的互斥锁而言其开销通常更小性能更高。
#include atomic
#include thread
#include cassert
using namespace std;static atomicint s_nCounter(0);void Increment()
{s_nCounter;
}int main()
{thread t1(Increment);thread t2(Increment);t1.join();t2.join();// 这个断言总是成立因为s_nCounter的递增是原子的assert(s_nCounter 2);return 0;
} std::atomic支持多种类型的对象包括但不限于基本内置类型、指针以及用户自定义类型如果满足特定条件。它提供了load、store、exchange、compare_exchange_strong/weak等一系列原子操作方法用于读写和更新其内部封装的数据成员。 std::future和std::async std::future 和 std::async是C 11标准引入的异步编程工具它们位于future头文件中用于简化并发任务的管理和结果的获取。 std::future是一个模板类它代表了一个可以在未来某个时间点获取的结果。当你启动一个异步计算时该计算的结果可以通过std::future对象来访问。std::future 提供了以下功能。 获取结果调用std::future::get()会阻塞当前线程直到异步计算完成并返回结果。 检查是否已准备好可以检查future是否已经包含有效结果或异常。 取消异步操作虽然不能直接取消异步操作但可以关联一个可取消的共享状态然后取消那个状态。 获取异常如果异步计算过程中抛出了异常则可以在future上捕获到这个异常。 std::async是一个函数模板它用来异步执行一个函数并返回一个表示其结果的std::future对象。
#include iostream
#include future
#include chrono
using namespace std;int LongTimeCompute(int nNumber)
{// 模拟耗时操作this_thread::sleep_for(chrono::seconds(5));return nNumber * nNumber;
}int main()
{// 使用async启动异步任务auto future_result async(launch::async, LongTimeCompute, 10);cout Main thread running... endl;// 当需要结果时调用get()int nResult future_result.get();cout Result of async computing: nResult endl;return 0;
} 在上面的示例代码中我们定义了一个模拟耗时计算的函数LongTimeCompute。然后在main函数中我们通过std::async创建了一个异步任务并指定其策略为std::launch::async确保在新的线程上运行该函数。 主线程在等待异步任务完成的同时可以继续执行其他任务当主线程需要得到异步任务的结果时它调用了 future_result.get()这将阻塞直到异步计算完成并将结果返回给主线程。最后主线程输出了异步计算得到的结果。 总结 C 11提供的并发特性不仅简化了多线程编程的复杂性而且增强了程序的安全性和可靠性。通过合理利用这些工具和技术我们能够更好地设计和实现适应现代多核架构的应用程序从而提升软件的整体性能表现。在实践中还需注意避免死锁、竞态条件等并发问题并结合实际情况选择适当的并发策略。正确理解和熟练运用C 11的并发库是构建高效、稳定且可扩展应用程序的关键。