在Linux操作系统中,C++11标准提供了强大的 多线程 支持,通过库可以方便地创建和管理线程,本文将详细介绍如何使用C++11的线程类来创建和管理多线程程序,包括可连接线程和分离线程的实例。
一、概念与基础
在Linux系统中,多线程编程是一种提高程序性能和响应能力的重要手段,线程是进程中的一个独立执行路径,多个线程可以共享进程的资源(如内存、文件句柄等),但每个线程都有自己的栈和寄存器,C++11引入了标准线程库,使得多线程编程更加简便和高效。
1. 线程的创建与管理
在C++11中,可以使用
std::thread
类来创建新线程,创建线程时需要指定一个函数或者函数对象,以及传递给该函数的参数,线程创建后可以立即开始执行指定的任务。
创建线程
:使用
std::thread
构造函数创建一个新线程,传入要执行的函数或函数对象及其参数。
#include#include using namespace std;void print_message(const char* msg) {cout << "Message: " << msg << endl;}int main() {thread t(print_message, "Hello, World!");t.join(); // 等待线程结束return 0;}
线程的连接与分离
:默认情况下,使用
std::thread
创建的线程是可连接的(joinable),这意味着主线程需要调用方法等待子线程完成,如果不需要等待子线程完成,可以将其设置为分离状态(detached),这样子线程将在后台独立运行,主线程无需等待。
// 分离线程示例thread t(print_message, "Goodbye, World!");t.detach(); // 设置为分离状态
2. 线程同步与互斥
多线程编程中的一个重要问题是数据竞争,即多个线程同时访问和修改共享数据,为了避免数据竞争,需要使用同步机制来协调线程之间的操作,C++11提供了多种同步机制,如 互斥锁 (mutex)、条件变量(condition_variable)等。
互斥锁
:使用
std::mutex
来保护共享数据,确保同一时间只有一个线程可以访问共享资源。
#include#include #include using namespace std;mutex mtx;void safe_increment(int& counter) {lock_guard lock(mtx);++counter;}int main() {int counter = 0;thread t1(safe_increment, ref(counter));thread t2(safe_increment, ref(counter));t1.join();t2.join();cout << "Final counter value: " << counter << endl; // 输出2return 0;}
条件变量
:使用
std::condition_variable
来实现线程间的复杂同步逻辑,如生产者-消费者问题。
#include#include #include #include using namespace std;mutex mtx;condition_variable cv;bool ready = false;void wait_For_ready() {unique_lock lock(mtx);cv.wait(lock, []{ return ready; });cout << "Thread is ready!" << endl;}void set_ready() {unique_lock lock(mtx);ready = true;cv.notify_one(); // 唤醒一个等待线程}int main() {thread t(wait_for_ready);this_thread::sleep_for(chrono::seconds(1)); // 模拟延迟set_ready();t.join();return 0;}
二、高级应用与实践
1. 线程池
线程池是一种常见的优化技术,用于减少频繁创建和销毁线程带来的开销,通过预先创建一定数量的线程,并将任务分配给这些线程执行,可以提高程序的性能和响应速度。
#include#include #include #include #include #include using namespace std;class ThreadPool {public:ThreadPool(size_t num_threads) : stop(false) {for (size_t i = 0; i < num_threads; ++i) {workers.emplace_back([this] {while (true) {function task;{unique_lock lock(this->queue_mutex);this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });if (this->stop && this->tasks.empty()) return;task = move(this->tasks.front());this->tasks.pop();}task();}});}}template void enqueue(F&& f) {{unique_lock lock(queue_mutex);if (stop) throw runtime_error("enqueue on stopped ThreadPool");tasks.emplace_back(forward (f));}condition.notify_one();}~ThreadPool() {{unique_lock lock(queue_mutex);stop = true;}condition.notify_all();for (thread &worker : workers) worker.join();}private:vector workers;queue > tasks;mutex queue_mutex;condition_variable condition;bool stop;};
使用示例:
int main() {ThreadPool pool(4); // 创建包含4个线程的线程池for (int i = 0; i < 8; ++i) {pool.enqueue([i] { cout << "Task " << i << endl; });}// ThreadPool析构时会自动等待所有任务完成return 0;}
2. 避免死锁与活锁
死锁 :当两个或多个线程相互等待对方释放资源时,就会发生死锁,为了避免死锁,应确保线程按照一致的顺序获取锁,或者使用超时机制(如)尝试获取锁。
活锁 :当多个线程不断竞争同一个资源时,可能会导致系统性能下降甚至崩溃,可以通过优化算法、减少锁的粒度或使用无锁数据结构来避免活锁。
三、常见问题与解答栏目
Q1: 如何在C++11中正确使用互斥锁来保护共享数据?
A1: 在C++11中,可以使用
std::mutex
和
std::lock_guard
或
std::unique_lock
来保护共享数据。
std::lock_guard
是一个作用域锁管理器,它在构造时自动加锁,在析构时自动解锁,适用于简单场景,而
std::unique_lock
则提供了更灵活的加锁和解锁方式,适用于需要手动控制锁生命周期的场景,下面是一个使用
std::mutex
和
std::lock_guard
保护共享数据的示例:
#include#include #include using namespace std;mutex mtx; // 全局互斥锁int shared_data = 0; // 共享数据void increment() {for (int i = 0; i < 1000; ++i) {lock_guard lock(mtx); // 自动加锁,作用域结束时自动解锁++shared_data; // 修改共享数据}}int main() {thread t1(increment); // 创建并启动线程t1thread t2(increment); // 创建并启动线程t2t1.join(); // 等待t1完成t2.join(); // 等待t2完成cout << "Final value of shared>
求助linux下创建多线程 为何不能多次创建
根据版本而定 老版本可能是先要执行子进程 最新的版本都是取决于进程调度算法 源代码:sched.c
c语言程序怎么运行
运行。如果是linux 或者 UNIX的话?什么环境?(问题表述不清楚)如果你借助vc++或者visual studio 这类的工具的话,新建工程.c(代码文件名) 然后在同一目录下执行 ./编译器是什么,gcc(cc) *,抒写代码,然后编译 即运行;a
linux终端下如何进行C语言编译
1、首先在linux下判断是否安装gcc编译器,直接执行:gcc -v,判断是否安装gcc。 2、然后需要在代码框内写一个简单的C源程序。 3、在程序中使用了C语言中的数学开方函数,sqrt(),所以需要引入math.h头文件。 4、写完源程序之后,我们对其进行编译,如果使用通常的编译语句,【gcc math.c -o math】进行编译,会出现错误;大概的意思就是不认识sqrt这个东西。 5、所以需要使用正确的命令:【gcc math.c -lm -o math】;这样才能编译正确。
发表评论