C++ 备忘清单 & cpp cheatsheet & 速查表

多线程介绍

g++编译选项:-std=c++11。包含头文件:

  • #include <thread>:C++多线程库
  • #include <mutex>:C++互斥量库
  • #include <future>:C++异步库

线程的创建

以普通函数作为线程入口函数:

void entry_1() { }
void entry_2(int val) { }

std::thread my_thread_1(entry_1);
std::thread my_thread_2(entry_2, 5);

以类对象作为线程入口函数:

class Entry
{
    void operator()() { }
    void entry_function() { }
};

Entry entry;
// 调用operator()()
std::thread my_thread_1(entry);
// 调用Entry::entry_function
std::thread my_thread_2(&Entry::entry_function, &entry);

以lambda表达式作为线程入口函数:

std::thread my_thread([]() -> void
      {
         // ...
      });

线程的销毁

thread my_thread;
// 阻塞
my_thread.join();
// 非阻塞
my_thread.detach();

this_thread

// 获取当前线程ID
std::this_thread::get_id();
// 使当前线程休眠一段指定时间
std::this_thread::sleep_for();
// 使当前线程休眠到指定时间
std::this_thread::sleep_until();
// 暂停当前线程的执行,让别的线程执行
std::this_thread::yield();

#include <mutex>

锁的基本操作

创建锁

std::mutex m;

上锁

m.lock();

解锁

m.unlock();

尝试上锁:成功返回true,失败返回false

m.try_lock();

解锁

m.unlock();

更简单的锁 —— std::lock_guard<Mutex>

构造时上锁,析构时解锁

std::mutex m;
std::lock_guard<std::mutex> lock(m);

额外参数:std::adopt_lock:只需解锁,无需上锁

// 手动上锁
m.lock();
std::lock_guard<mutex> lock(m,
    std::adopt_lock);

unique_lock<Mutex>

构造上锁,析构解锁

std::mutex m;
std::unique_lock<mutex> lock(m);
std::adopt_lock

只需解锁,无需上锁

// 手动上锁
m.lock();
std::unique_lock<mutex> lock(m,
    std::adopt_lock);
std::try_to_lock

尝试上锁,可以通过std::unique_lock<Mutex>::owns_lock()查看状态

std::unique_lock<mutex> lock(m,
    std::try_to_lock);
if (lock.owns_lock())
{
    // 拿到了锁
}
else
{
    // 没有
}
std::defer_lock

绑定锁,但不上锁

std::unique_lock<mutex> lock(m,
    std::defer_lock);
lock.lock();
lock.unlock();
std::unique_lock<Mutex>::release

返回所管理的mutex对象指针,**释放所有权。**一旦释放了所有权,那么如果原来互斥量处于互斥状态,程序员有责任手动解锁。

std::call_once

当多个线程通过这个函数调用一个可调用对象时,只会有一个线程成功调用。

std::once_flag flag;

void foo() { }

std::call_once(flag, foo);

std::condition_variable

创建条件变量

std::condition_variable cond;

等待条件变量被通知

std::unique_lock<std::mutex>
    lock;
extern bool predicate();

// 调用方式 1
cond.wait(lock);
// 调用方式 2
cond.wait(lock, predicate);

  • wait不断地尝试重新获取并加锁该互斥量,如果获取不到,它就卡在这里并反复尝试重新获取,如果获取到了,执行流程就继续往下走
  • wait在获取到互斥量并加锁了互斥量之后:
    • 如果wait被提供了可调用对象,那么就执行这个可调用对象:
      • 如果返回值为false,那么wait继续加锁,直到再次被 notified
      • 如果返回值为true,那么wait返回,继续执行流程
    • 如果wait没有第二个参数,那么直接返回,继续执行

std::condition_variable::notify_one

notify_one 唤醒一个调用 wait 的线程。注意在唤醒之前要解锁,否则调用 wait 的线程也会因为无法加锁而阻塞。

std::condition_variable::notify_all

唤醒所有调用 wait 的线程。

获取线程的运行结果

#include <future>

创建异步任务

double func(int val);

// 使用std::async创建异步任务
// 使用std::future获取结果
// future模板中存放返回值类型
std::future<double> result =
    std::async(func, 5);

获取异步任务的返回值

等待异步任务结束,但是不获取返回值:

result.wait();

获取异步任务的返回值:

int val = result.get();

注:

  • get()返回右值,因此只可调用一次
  • 只要调用上述任意函数,线程就会一直阻塞到返回值可用(入口函数运行结束)

std::async 的额外参数

额外参数可以被放在 std::async 的第一个参数位置,用于设定 std::async 的行为:

  • std::launch::deferred:入口函数的运行会被推迟到std::future<T>::get()或者std::future<T>::wait()被调用时。此时调用线程会直接运行线程入口函数,换言之,不会创建子线程
  • std::launch::async:立即创建子线程,并运行线程入口函数
  • std::launch::deferred | std::launch::async:默认值,由系统自行决定

返回值的状态

让当前线程等待一段时间(等待到指定时间点),以期待返回值准备好:

extern double foo(int val) {}

std::future<double> result =
    std::async(foo, 5);

//返回值类型
std::future_status status;
// 等待一段时间
status = result.wait_for(
  std::chrono::seconds(1)
  );
// 等待到某一时间点
status = result.wait_until(
  std::chrono::now() +
    std::chrono::seconds(1)
  );

在指定的时间过去后,可以获取等待的结果:

// 返回值已经准备好
if (status ==
     std::future_status::ready)
{

}
// 超时:尚未准备好
else if (status ==
    std::future_status::timeout)
{ }
// 尚未启动: std::launch::deferred
else if (status ==
    std::future_status::deferred)
{ }

多个返回值

如果要多次获取结果,可以使用std::shared_future,其会返回结果的一个拷贝

std::shared_future<T> result;

对于不可拷贝对象,可以在std::shared_future中存储对象的指针,而非指针本身。

创建线程

void threadFunction() {
  // 线程函数体
  std::cout << "From thread" << std::endl;
}

int main() {
  // 创建线程并开始执行线程函数
  std::thread t(threadFunction);
  
  // 等待线程执行完毕
  t.join();
  
  return 0;
}

传递参数给线程函数

void threadFunction(int value) {
  // 线程函数体
  std::cout << "Received value: " << value << std::endl;
}

int main() {
  int data = 42;
  std::thread t(threadFunction, data);
  t.join();
  return 0;
}

使用Lambda表达式创建线程

int main() {
  int data = 42;
  std::thread t([data]() {
      // Lambda 表达式作为线程函数
      std::cout << "Received value: " << data << std::endl;
  });
  t.join();
  return 0;
}

处理线程间的同步:

#include <mutex>

std::mutex mtx;

void threadFunction() {
  std::lock_guard<std::mutex> lock(mtx);
  std::cout << "Thread safe output." << std::endl;
}

int main() {
  std::thread t1(threadFunction);
  std::thread t2(threadFunction);
  t1.join();
  t2.join();
  return 0;
}

使用std::async启动异步任务:

#include <future>

int taskFunction() {
  // 异步任务
  return 42;
}

int main() {
  // 启动异步任务
  std::future<int> fut = std::async(std::launch::async, taskFunction);
  
  // 获取异步任务的结果
  int result = fut.get();
  
  std::cout << "Result: " << result << std::endl;
  return 0;
}