【C++11】 线程库的使用(二)

简介: 【C++11】 线程库的使用(二)

3 原子操作

C++11中引入了原子操作。所谓原子操作:即不可被中断的一个或一系列操作,C++11引入的原子操作类型,使得线程间数据的同步变得非常高效。


b16a412546b64ca9af22c71b81394f4c.png👉【atomic】👈


比如之前我们抢票的代码还可以这样写:

atomic<long long> tickets;
mutex mtu;
int main()
{
  thread t1([] {
    for (int i = 0; i < 100000; ++i)
    {
      tickets++;
    }
    });
  thread t2([] {
    for (int i = 0; i < 100000; ++i)
    {
      tickets++;
    }
    });
  t1.join();
  t2.join();
  cout << tickets << endl;
  return 0;
}

运行结果:


29f2656067a442dabb3cc75807871917.png

我们使用原子操作不用加锁也可以正确的完成。其实原子操作的底层是用的CAS原子操作来完成的。

在C++11中,程序员不需要对原子类型变量进行加锁解锁操作,线程能够对原子类型变量互斥的

访问。更为普遍的,程序员可以使用atomic类模板,定义出需要的任意原子类型。

atmoic<T> t;    // 声明一个类型为T的原子类型变量t

注意:原子类型通常属于"资源型"数据,多个线程只能访问单个原子类型的拷贝,因此在C++11中,原子类型只能从其模板参数中进行构造,不允许原子类型进行拷贝构造、移动构造以及operator=等,为了防止意外,标准库已经将atmoic模板类中的拷贝构造、移动构造、赋值运算符重载默认删除掉了。

4 条件变量

先来看看condition_variable类中有哪些成员函数?

df65ec51afae44f88ba5965922eca81a.png

这个用法与Linux中基本一致,我在这里就不再多讲了。

现在假如我们要实现下面的场景,我们究竟应该怎么做?

支持两个线程交替打印,一个打印奇数,一个打印偶数

这样实现有两个注意点:

  • 1️⃣要保证一个线程先运行,一个后运行;
  • 2️⃣要防止一个线程不断运行。
  • 所以我们就可以写出下面的代码:
int num = 1;
mutex mtu;
condition_variable cdv;
int main()
{
  thread t1([=] {
    for (;num<100;num++)
    {
      unique_lock<mutex> lock(mtu);
      if (num % 2 == 0)
        cdv.wait(lock);
      cout << this_thread::get_id() << ":" << num << endl;
      cdv.notify_one();
    }
    });
  thread t2([] {
    for (; num<100; num ++)
    {
      unique_lock<mutex> lock(mtu);
      if(num%2!=0)
        cdv.wait(lock);
      cout << this_thread::get_id() << ":" << num << endl;
      cdv.notify_one();
    }
    });
  t1.join();
  t2.join();
  return 0;
}

但是上面的代码可能会存在着问题,因为在条件判断的时候我们没有加锁,这就导致有可能其中一个线程打印出了101这样的数据,所以我们可以写一个死循环,在循环体里面判断即可。

thread t1([=] {
    for (; ;num++)
    {
      unique_lock<mutex> lock(mtu);
      if (num >= 100) break;
      if (num % 2 == 0)
        cdv.wait(lock);
      cout << this_thread::get_id() << ":" << num << endl;
      cdv.notify_one();
    }
    });
  thread t2([] {
    for (; ; num++)
    {
      unique_lock<mutex> lock(mtu);
      if (num > 100)  break;
      if(num%2!=0)
        cdv.wait(lock);
      cout << this_thread::get_id() << ":" << num << endl;
      cdv.notify_one();
    }
    });


目录
相关文章
|
6天前
|
算法 C++ 容器
C++标准库(速查)总结
C++标准库(速查)总结
33 6
|
18天前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
77 10
|
13天前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
32 1
C++ 多线程之初识多线程
|
6天前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
36 5
|
6天前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
21 1
|
13天前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
36 6
|
16天前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
|
13天前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
16 0
C++ 多线程之线程管理函数
|
13天前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
15 0
Linux C/C++之线程基础
|
28天前
|
编译器 API C语言
超级好用的C++实用库之跨平台实用方法
超级好用的C++实用库之跨平台实用方法
32 6