【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();
    }
    });


Fox!
+关注
目录
打赏
0
0
0
0
2
分享
相关文章
超级好用的C++实用库之服务包装类
通过本文对Boost.Asio、gRPC和Poco三个超级好用的C++服务包装类库的详细介绍,开发者可以根据自己的需求选择合适的库来简化开发工作,提高代码的效率和可维护性。每个库都有其独特的优势和适用场景,合理使用这些库可以极大地提升C++开发的生产力。
47 11
|
3月前
|
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
180 7
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
4月前
|
C++标准库(速查)总结
C++标准库(速查)总结
110 6
|
4月前
|
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
99 5
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
96 1
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
165 6
|
4月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
62 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等