c++11线程、互斥量、条件变量等

简介: c++11线程、互斥量、条件变量等

一、线程thread

使用std::thread创建线程,提供线程入口函数或者函数对象,同时还可以指定线程函数的参数。

1. 使用举例
1.1 线程入口函数是普通函数
#include <iostream> 
#include <thread> // 需要包含头文件 
using namespace std;
// 1. 不传值
void func1() { 
  cout << "func1 into" << endl; 
}
// 2. 传入两个参数
void func2(int a, int b) { 
  cout << "func2 a + b = " << a+b << endl; 
}
// 3. 传引用
void func3(int &val) { 
  cout << "func3 c = " << &val << endl;
  val += 20;
}
// 4. detach 测试
void func4() { 
  cout << "func4 into sleep " << endl; 
  std::this_thread::sleep_for(std::chrono::seconds(1)); 
  cout << "func4 leave " << endl;
}
// 5. move
void func5() { 
  cout << "func5 into sleep " << endl; 
  std::this_thread::sleep_for(std::chrono::seconds(1)); 
  cout << "func5 leave " << endl;
}
int main()
{
  cout << "---------- main1 ----------------------" << endl;
  std::thread t1(func1);
  t1.join();  // join阻塞等待线程返回,线程若是不结束,会阻塞在这行代码
  cout << "\n---------- main2 ----------------------" << endl;
  int a = 5, b = 6;
  std::thread t2(func2, a, b);
  t2.join();
  cout << "\n---------- main3 ----------------------" << endl;
  int c = 10;
  std::thread t3(func3, std::ref(c));
  t3.join();
  cout << "main3 c = " << &c << ", "<<c << endl;
  cout << "\n---------- main4 ----------------------"<<c << endl;
  std::thread t4(func4);
  t4.detach(); // detach 设置分离属性,设置后t4线程独立运行,待主线程退出后回收相关资源
  std::this_thread::sleep_for(std::chrono::seconds(2)); // 如果不休眠,主线程会直接退出,导致线程4来不及执行就退出
  // 5 .move 
  cout << "\n\n -------- main5--------------------------\n"; 
  thread t5_1(func5); 
  thread t5_2(std::move(t5_1)); // t5_1 线程失去所有权 
  // t5_1.join(); // t5_1 线程失去所有权,再去join 抛出异常 程序异常退出
  t5_2.join();
  return 0;
}

运行结果:

1.2 线程入口函数是类的成员函数
#include <iostream> 
#include <thread> // 需要包含头文件 
using namespace std;
class A {
public: 
  // 4. 传入类函数 
  void func4(int a) { 
    // std::this_thread::sleep_for(std::chrono::seconds(1)); 
    cout << "thread:" << name_<< ", fun4 a = " << a << endl; 
  }
  void setName(string name) { 
    name_ = name; 
  }
  void displayName() { 
    cout << "this:" << this << ", name:" << name_ << endl; 
  }
  void play() { 
    std::cout<<"play call!"<<std::endl; 
  } 
private: 
  string name_; 
};
int main()
{
  cout << "---------- main1 ----------------------"<< endl;
  // A *aptr = new A();
  // aptr->setName("lwang");
  // std::thread t1(&A::func4, aptr, 20);
  A a;
  a.setName("lwang");
  std::thread t1(&A::func4, a, 20);
  t1.join();  
  return 0;
}

运行结果:

二、互斥量mutex

2.1 作用

互斥量是多线程间访问某一共享变量时,保证变量可以被安全访问。mutex又称互斥量,是独占的互斥量。C++ 11中与 mutex相关的类(包括锁类型)和函数都声明在 mutex 头文件中,所以如果你需要使用 std::mutex,就必须包含 mutex 头文件。

2.2 使用举例
#include <iostream> // std::cout 
#include <thread> // std::thread 
#include <mutex> // std::mutex 
volatile int counter(0); // non-atomic counter 
std::mutex mtx; // locks access to counter 
void increases_10k_lock() 
{ 
  for (int i=0; i<10000; ++i) {   
    mtx.lock(); 
    ++counter; 
    mtx.unlock(); 
  }
}
void increases_10k_try_lock() 
{ 
  for (int i=0; i<10000; ++i) { 
    if (mtx.try_lock()) { 
      // only increase if currently not locked: // 
      ++counter; // 
      mtx.unlock(); // 
    } 
  }
}
int main() { 
  std::thread threads[10]; 
  for (int i=0; i<10; ++i) 
    threads[i] = std::thread(increases_10k_try_lock); 
  for (auto& th : threads) 
    th.join(); 
  std::cout << "try_lock counter: " << counter << std::endl; 
  counter = 0; 
  std::thread threadId[10]; 
  for (int i=0; i<10; ++i) 
    threadId[i] = std::thread(increases_10k_lock); 
  for (auto& th : threadId) 
    th.join(); 
  std::cout << "lock counter: " << counter << std::endl; 
  return 0; 
}

运行结果:

分析:try_lock() 如果在尝试获取锁的时候拿不到锁,就不能对counter加1,导致结果异常。使用 lock() 可以实现让counter正常加到10w。

三、条件变量

互斥量可以保证共享变量在多线程访问的安全性,但是无法实现线程的同步。线程同步指的我们希望线程按照我们预期的顺序执行的行为。c++11提供了对该行为的支持,就是条件变量。需要包含头文件:condition_variable

3.1 代码举例

使用条件变量实现一个同步队列,同步队列作为一个线程安全的数据共享区,经常用于线程之间数据读取。

四、原子变量

#include <iostream> 
#include <atomic> 
#include <thread> 
// std::atomic<int> count = 0;//错误初始化
std::atomic<int> count(0); // 准确初始化 
void set_count(int x) 
{ 
  std::cout << "set_count:" << x << std::endl; 
  count.store(x, std::memory_order_relaxed); // set value atomically 
}
void print_count() 
{ 
  int x; 
  do {
    x = count.load(std::memory_order_relaxed); // get value atomically 
  } while (x==0);
  std::cout << "count: " << x << '\n'; 
}
int main () { 
  std::thread t1 (print_count); 
  std::thread t2 (set_count, 10); 
  t1.join(); 
  t2.join(); 
  std::cout << "main finish\n"; 
  return 0; 
}

运行结果:

五、lock_guard和unique_lock

5.1 lock_guard

为了防止忘记解锁,C++11引入了一个叫做 std::lock_guard 的类模板。std::lock_guard 可以直接取代 lock() 和 unlock(),也就说使用 std::lock_guard 后,就不能再使用 lock() 和 unlock() 了。

std::lock_guard 在构造函数中加锁,在析构函数中解锁。

#include <iostream> // std::cout 
#include <thread> // std::thread 
#include <mutex> // std::mutex, std::lock_guard 
#include <stdexcept> // std::logic_error 
std::mutex mtx; 
void print_even (int x) { 
  if (x % 2 == 0) std::cout << x << " is even\n"; 
  else throw (std::logic_error("this thread param not even")); 
}
void print_thread_id (int id) { 
  try {
    std::lock_guard<std::mutex> lck (mtx); 
    print_even(id); 
  }
  catch (std::logic_error&) { 
    std::cout << "[exception caught]\n";
  } 
}
int main () { 
  std::thread threads[10]; // spawn 10 threads: 
  for (int i=0; i<10; ++i) 
    threads[i] = std::thread(print_thread_id,i+1); 
  for (auto& th : threads) th.join(); 
  return 0; 
}

运行结果:

std::lock_guard 虽然用起来方便,但是不够灵活,它只能在析构函数中 unlock(),也就是对象被释放的时候,这通常是在函数返回的时候,或者通过添加代码块 { /* 代码块 */ } 限定作用域来指定释放时机。

5.1 unique_lock

unique_lock 也是类模板,功能和lock_guard类似。unique_lock 虽然率会低一点,内存占用量相对高一些,但是比lock_guard更灵活。uniqie_lock 的缺省用法实际上与 lock_quard 一样,可以直接替换。

#include <iostream> 
#include <deque> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <unistd.h> 
std::deque<int> q; 
std::mutex mu; 
std::condition_variable cond; 
int count = 0; 
void fun1() 
{ 
  while (true) 
  { 
    { 
      // std::lock_guard<std::mutex> locker(mu);
      std::unique_lock<std::mutex> locker(mu); 
      q.push_front(count++); 
      // locker.unlock(); // 这里是不是必须的?
      cond.notify_one(); // 
    } 
    sleep(1); 
  } 
}
void fun2() 
{ 
  while (true) 
  { 
    std::unique_lock<std::mutex> locker(mu); 
    cond.wait(locker, [](){return !q.empty();}); 
    auto data = q.back(); 
    q.pop_back(); // 
    // locker.unlock(); // 这里是不是必须的? 
    std::cout << "thread2 get value form thread1: " << data << std::endl; 
  } 
}
int main() { 
  std::thread t1(fun1); 
  std::thread t2(fun2); 
  t1.join(); 
  t2.join(); 
  return 0; 
}

六、异步操作

6.1 std::future 和 std::aysnc 使用举例

std::future 异步获取任务函数的返回值;std::aysnc 异步执行某个任务函数

#include <iostream> 
#include <future> 
#include <thread> 
using namespace std; 
int find_result_to_add() {
  std::this_thread::sleep_for(std::chrono::seconds(2)); // 用来测试异步延迟的影 响 
  std::cout << "find_result_to_add" << std::endl; 
  return 1 + 1; 
}
string find_result_string() {
  std::this_thread::sleep_for(std::chrono::seconds(2)); // 用来测试异步延迟的影 响 
  std::cout << "find_result_string" << std::endl; 
  return "lwang"; 
}
int find_result_to_add2(int a, int b) {
  std::this_thread::sleep_for(std::chrono::seconds(5)); // 用来测试异步延迟的影 响 
  return a + b; 
}
string find_result_to_string2(string val) {
  std::this_thread::sleep_for(std::chrono::seconds(5)); // 用来测试异步延迟的影 响 
  return val; 
}
void do_other_things() { 
  std::cout << "do_other_things" << std::endl; 
  std::this_thread::sleep_for(std::chrono::seconds(5)); // 模拟其他任务耗时
}
int main() { 
  // 三种写法 std::async(func(),Args)  异步执行func函数,自己内部有线程
  // std::future<int> result = std::async(find_result_to_add);  // 第一种
  // std::future<decltype (find_result_string())> result = std::async(find_result_string);  // 第二种
  auto result = std::async(find_result_to_add); // 推荐的写法  第三种
  do_other_things(); 
  std::cout << "result: " << result.get() << std::endl; // 这里get()会阻塞, 等待函数的返回值。
  // 如果任务函数有参数,参数不能写类型,需要写具体类型的值!!!
  // std::future<decltype (find_result_to_add2(int, int))> result2 = std::async(find_result_to_add2, 10, 20); //错误 
  std::future<decltype (find_result_to_add2(0, 0))> result2 = std::async(find_result_to_add2, 10, 20); 
  std::cout << "result2: " << result2.get() << std::endl; 
  // std::future<decltype (find_result_to_string2(string))> result3 = std::async(find_result_to_string2, "lwang");  //错误 
  std::future<decltype (find_result_to_string2(""))> result3 = std::async(find_result_to_string2, "lwang"); 
  std::cout << "result3: " << result3.get() << std::endl; // 延迟是否有影响? 
  std::cout << "main finish" << endl; 
  return 0; 
}
6.2 std::packaged_task 使用举例

std::packaged_task 将任务和feature绑定在一起的模板,对任务的封装

#include <iostream> 
#include <future> 
using namespace std; 
int add(int a, int b, int c) { 
  std::cout << "call add\n"; 
  return a + b + c; 
}
void do_other_things() { 
  std::cout << "do_other_things" << std::endl; 
  std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main() { 
  std::packaged_task<int(int, int, int)> task(add); // 封装任务,还没有执行 
  do_other_things(); 
  std::future<int> result = task.get_future(); 
  task(1, 1, 2); //必须执行任务,否则在get()获取future的值时会一直阻塞 
  std::cout << "result:" << result.get() << std::endl;
  return 0; 
}
6.3 promise 使用举例

作用:std::promise 提供了一种设置值的方式,通过相关联的future对象可以读取设置值。

#include <future> 
#include <string> 
#include <thread> 
#include <iostream> 
using namespace std; 
void print(std::promise<std::string>& p) { 
  p.set_value("There is the result whitch you want."); 
}
void do_some_other_things() { 
  std::cout << "Hello World" << std::endl; 
}
int main() { 
  std::promise<std::string> promise; 
  std::future<std::string> result = promise.get_future(); 
  std::thread t(print, std::ref(promise));  // 创建线程t,去设置promise对象的值
  do_some_other_things(); 
  std::cout << result.get() << std::endl;  // future 获取 promise 返回值
  t.join();
}

运行结果:

分析:在 promise 创建好的时候,future也创建好了。主线程持有future,阻塞等待,随时获取值。

文章参考于<零声教育>的C/C++linux服务期高级架构

相关文章
|
2天前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
1月前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
65 7
|
1月前
|
消息中间件 存储 安全
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
88 6
|
2月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
87 5
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
45 0
|
2月前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
35 0
Linux C/C++之线程基础
|
2月前
|
C语言 C++
实现两个变量值的互换[C语言和C++的区别]
实现两个变量值的互换[C语言和C++的区别]
29 0