在 C++ 中,线程是用来实现并发编程的重要工具,它允许程序同时执行多个任务。C++11 标准引入了多线程支持,主要通过 <thread> 标头文件提供相关功能,包括线程创建、管理和同步等。


1. 基本概念

  • 线程是程序执行的基本单位,一个程序可以包含多个线程。
  • 多线程可以提高程序效率,特别是在多核 CPU 上,每个线程可以在不同的核上运行。
  • C++ 提供的多线程功能包括:
    • 线程的创建与管理(std::thread)。
    • 同步机制(如互斥锁 std::mutex、条件变量 std::condition_variable 等)。
    • 数据保护(如线程安全的操作)。

2. 创建和管理线程

1) 创建线程

线程可以通过 std::thread 创建,并且可以使用函数、lambda 表达式或可调用对象作为线程入口。

(a) 使用普通函数

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <thread>

void threadFunction() {
std::cout << "Thread is running..." << std::endl;
}

int main() {
std::thread t(threadFunction); // 创建线程
t.join(); // 等待线程执行完毕
return 0;
}
  • **std::thread t(threadFunction)**:创建线程并执行 threadFunction
  • **t.join()**:主线程等待 t 线程执行完成。

(b) 使用 lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <thread>

int main() {
std::thread t([] {
std::cout << "Thread running with lambda!" << std::endl;
});

t.join(); // 等待线程结束
return 0;
}

(c) 使用类的成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <thread>

class Worker {
public:
void operator()() {
std::cout << "Thread running with functor!" << std::endl;
}
};

int main() {
Worker worker;
std::thread t(worker); // 创建线程并执行 Worker 的重载函数
t.join(); // 等待线程结束
return 0;
}

2) 分离线程(detach

分离线程允许主线程与新线程独立运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <thread>
#include <chrono>

void threadFunction() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Detached thread completed!" << std::endl;
}

int main() {
std::thread t(threadFunction);
t.detach(); // 分离线程
std::cout << "Main thread continues..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
return 0;
}

注意:分离的线程在程序结束前需要完成,否则会出现未定义行为。


3) 获取线程 ID

1
2
std::thread::id id = std::this_thread::get_id();
std::cout << "Thread ID: " << id << std::endl;

3. 线程同步

由于线程是并发执行的,因此多个线程访问共享资源时可能会导致数据竞争(Race Condition)。C++ 提供了多种同步工具来避免这种问题。

1) 互斥锁(std::mutex

互斥锁用于保证只有一个线程可以访问共享资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void printMessage(const std::string& message) {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁和解锁
std::cout << message << std::endl;
}

int main() {
std::thread t1(printMessage, "Thread 1: Hello");
std::thread t2(printMessage, "Thread 2: World");

t1.join();
t2.join();
return 0;
}
  • **std::lock_guard**:管理锁的生命周期,防止忘记解锁。
  • **mtx.lock()mtx.unlock()**:手动加锁和解锁,但可能导致死锁,不推荐使用。

2) 条件变量(std::condition_variable

条件变量用于实现线程间的协调,例如一个线程等待某个条件成立时再执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>

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

void worker() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; }); // 等待条件成立
std::cout << "Worker thread is running!" << std::endl;
}

void notify() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true; // 修改条件
}
cv.notify_one(); // 通知一个等待线程
}

int main() {
std::thread t1(worker);
std::thread t2(notify);

t1.join();
t2.join();
return 0;
}

3) 原子操作(std::atomic

原子操作用于多线程中对单个变量的操作,避免数据竞争。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>
#include <atomic>

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

void increment() {
for (int i = 0; i < 1000; ++i) {
++counter; // 原子操作,线程安全
}
}

int main() {
std::thread t1(increment);
std::thread t2(increment);

t1.join();
t2.join();

std::cout << "Final counter: " << counter << std::endl;
return 0;
}

4. 注意事项

  1. 数据竞争:多个线程同时修改共享资源可能导致错误,需要使用同步机制。
  2. 死锁:多个线程互相等待资源,导致程序无法继续。
  3. 分离线程的风险:主线程结束时,分离的线程未完成可能会导致崩溃。
  4. 性能开销:线程切换会带来一定的系统开销。

5. 总结

  • 线程创建std::thread 提供简单易用的接口。
  • 线程同步:使用 std::mutexstd::condition_variable 等工具保护共享数据。
  • 线程安全:推荐使用 std::atomic 或其他同步机制避免数据竞争。

线程是 C++ 中实现并发的核心工具,但需要小心处理共享资源和线程生命周期,确保程序的正确性和性能。