21xrx.com
2024-12-23 02:11:42 Monday
登录
文章检索 我的文章 写文章
C++多线程学习笔记
2023-07-01 16:20:33 深夜i     --     --
C++ Multi-threading 学习笔记

C++是计算机科学领域中非常重要的一门编程语言,它支持多线程编程,这使得它在编写高性能、并发的程序时非常有用。然而,多线程编程往往比单线程编程更复杂和困难,需要更多的注意事项和技能。在本篇文章中,我们将介绍C++多线程编程的基本知识和技巧。

1. 多线程基础

多线程是指程序可以同时执行多个任务。在C++中,可以使用std::thread类创建一个新线程。如下所示,创建一个新线程并执行一个函数:


#include <iostream>

#include <thread>

void hello() {

 std::cout << "Hello Concurrent World\n";

}

int main() {

 std::thread t(hello);

 t.join(); // 等待t线程执行完毕

}

在这个例子中,我们创建了一个新的线程t,它执行hello函数。通过调用t.join()函数,主线程将等待直到t线程执行完毕。

2. 互斥锁

多线程执行时,会出现多个线程同时访问同一个变量的情况。如果多个线程对同一个变量执行写操作,那么结果可能是不确定的。为了解决这个问题,可以使用互斥锁来同步线程的访问。

互斥锁是同步多线程访问共享变量的机制。在C++中,可以使用std::mutex类来实现互斥锁。如下所示,访问共享变量时,先使用锁来保证线程安全:


#include <iostream>

#include <thread>

#include <mutex>

std::mutex mtx; // 定义一个互斥锁

void print_block(int n, char c) {

  mtx.lock(); // 占用互斥锁

  for (int i = 0; i < n; ++i)

    std::cout << c;

  

  std::cout << std::endl;

  mtx.unlock(); // 释放互斥锁

}

int main() {

  std::thread t1(print_block, 10, '*');

  std::thread t2(print_block, 10, '#');

  t1.join();

  t2.join();

  return 0;

}

在这个例子中,我们使用了互斥锁来保证线程安全。在print_block函数中,首先使用mtx.lock()锁住互斥锁,以保证同时只有一个线程可以访问共享变量。当打印操作完成后,使用mtx.unlock()释放互斥锁,以便其他线程可以访问共享变量。

3. 条件变量

条件变量可以用来解决多个线程协作的问题。当一个线程需要等待某个条件成立时,它可以使用条件变量来挂起自己,并等待另一个线程通知它条件已经成立。在C++中,可以使用std::condition_variable类来实现条件变量。

如下所示,使用条件变量来进行线程间协作:


#include <iostream>

#include <thread>

#include <mutex>

#include <condition_variable>

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 running\n";

}

void notifier() {

  std::this_thread::sleep_for(std::chrono::seconds(1));

  ready = true; // 设置条件成立

  cv.notify_one(); // 通知等待的线程

}

int main() {

  std::thread t1(worker);

  std::thread t2(notifier);

  t1.join();

  t2.join();

  return 0;

}

在这个例子中,我们创建了一个线程worker,它需要等待一个条件ready成立后才能执行任务。notifier线程在等待1秒后将ready标记为true,并且使用cv.notify_one()来通知worker线程已经准备好了。

4. 避免死锁

在多线程编程中,死锁是一个经常遇到的问题。死锁指的是两个或多个线程互相持有对方需要的资源而阻塞。为了避免死锁,可以避免使用多个互斥锁,或者总是以相同的顺序获取锁。

如下所示,演示了死锁的情况:


#include <iostream>

#include <thread>

#include <mutex>

std::mutex mtx1, mtx2;

void foo1() {

  std::unique_lock<std::mutex> lock1(mtx1);

  std::cout << "foo1 lock mtx1\n";

  std::this_thread::sleep_for(std::chrono::seconds(1));

  std::unique_lock<std::mutex> lock2(mtx2);

  std::cout << "foo1 lock mtx2\n";

}

void foo2() {

  std::unique_lock<std::mutex> lock2(mtx2);

  std::cout << "foo2 lock mtx2\n";

  std::this_thread::sleep_for(std::chrono::seconds(1));

  std::unique_lock<std::mutex> lock1(mtx1);

  std::cout << "foo2 lock mtx1\n";

}

int main() {

  std::thread t1(foo1);

  std::thread t2(foo2);

  t1.join();

  t2.join();

  return 0;

}

在这个例子中,foo1线程首先获取mtx1互斥锁,然后睡眠1秒钟,等待foo2线程获取mtx2互斥锁。foo2线程也会在运行到一半时睡眠1秒钟,等待foo1线程获取mtx1互斥锁。这两个线程因此陷入了死锁状态,无法继续执行。

5. 总结

本篇文章介绍了C++多线程编程的基础知识和技巧。多线程编程可以提高程序的并发性和性能,但也会带来很多问题,如竞态条件和死锁。为了避免这些问题,需要仔细考虑线程安全性,并使用互斥锁和条件变量等工具来保证线程安全和线程间通讯。在实践中,建议使用std::thread、std::mutex和std::condition_variable等标准库组件来编写多线程程序。

  
  
下一篇: C++11的使用

评论区

{{item['qq_nickname']}}
()
回复
回复