1. 并发编程的概念
并发编程是指在一个程序中同时运行多个独立的部分,这些独立的部分可以是函数、线程或进程,它们争夺计算机中的资源来完成任务。并发编程使得程序能够更加高效地利用计算机的多核心能力,以提高程序的运行效率。
2. 并发编程中的问题
2.1 竞态条件
在并发编程中,多个线程可能会同时访问同一个共享资源,例如一个变量。如果没有合适的同步机制,这些线程就会产生竞态条件。竞争条件的结果是不确定的,可能会导致程序出现错误结果。
竞态条件的解决方法是使用互斥锁或信号量等同步机制。互斥锁可以防止多个线程同时访问同一个共享资源,信号量可以控制同时访问共享资源的线程数量。以下是一个使用互斥锁解决竞态条件的例子:
#include
#include
#include
std::mutex mtx; // 互斥锁
void print(int num) {
mtx.lock(); // 加锁
std::cout << "Thread " << num << " is printing" << std::endl;
mtx.unlock(); // 解锁
}
int main() {
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread(print, i);
}
for (auto &t : threads) {
t.join();
}
return 0;
}
2.2 死锁
死锁是指两个或多个线程等待对方释放资源,导致它们都无法继续执行的情况。例如,线程 A 持有锁 1,请求锁 2,线程 B 持有锁 2,请求锁 1,那么它们就会形成死锁。
死锁的解决方法是避免线程之间出现相互等待的情况。例如,可以按照固定顺序请求锁,或者使用超时机制。
3. 并发编程中的常用库
3.1 std::thread
std::thread 是 C++11 标准中提供的库,可用于创建线程。以下是一个使用 std::thread 创建线程的例子:
#include
#include
void hello() {
std::cout << "Hello concurrency" << std::endl;
}
int main() {
std::thread t(hello);
t.join();
return 0;
}
在上面的例子中,std::thread 构造函数接受一个函数指针,用于指定线程的执行函数。join() 方法用于等待线程结束。
3.2 std::mutex
std::mutex 是 C++11 标准中提供的互斥锁。以下是一个使用 std::mutex 保护共享资源的例子:
#include
#include
#include
std::mutex mtx; // 互斥锁
int balance = 1000;
void withdraw(int amount) {
mtx.lock(); // 加锁
if (balance >= amount) {
balance -= amount;
std::cout << "Withdraw " << amount << ", balance: " << balance << std::endl;
} else {
std::cout << "Withdraw failed, balance: " << balance << std::endl;
}
mtx.unlock(); // 解锁
}
int main() {
std::thread t1(withdraw, 100);
std::thread t2(withdraw, 200);
t1.join();
t2.join();
return 0;
}
在上面的例子中,mtx.lock() 和 mtx.unlock() 方法用于加锁和解锁互斥锁,从而保护 balance 这个共享资源。
3.3 std::condition_variable
std::condition_variable 是 C++11 标准中提供的条件变量,它能够等待另一个线程发出的信号并做出相应的反应。以下是一个使用 std::condition_variable 实现生产者-消费者模型的例子:
#include
#include
#include
#include
#include
std::mutex mtx;
std::condition_variable cv;
std::queue q;
void producer(int id) {
for (int i = 0; i < 5; ++i) {
{
std::unique_lock lock(mtx);
q.push(i);
std::cout << "Producer " << id << " produced " << i << std::endl;
}
cv.notify_all(); // 发送信号
}
}
void consumer(int id) {
while (true) {
std::unique_lock lock(mtx);
cv.wait(lock, []{ return !q.empty(); }); // 等待信号
int val = q.front();
q.pop();
std::cout << "Consumer " << id << " consumed " << val << std::endl;
lock.unlock();
}
}
int main () {
std::thread t1(producer, 1);
std::thread t2(consumer, 1);
std::thread t3(consumer, 2);
t1.join();
t2.join();
t3.join();
return 0;
}
在上面的例子中,使用 std::condition_variable 对消费者线程进行等待,并在生产者线程中使用 cv.notify_all() 发送信号。
总结
并发编程能够提高程序运行效率,但同时也带来了问题。在进行并发编程时,需要注意避免竞态条件和死锁等问题,并使用常用的并发库(如 std::thread、std::mutex 和 std::condition_variable 等)进行线程同步和通信。