当前位置: 首页 > wzjs >正文

扁平化企业网源码win8风格精简化源码asp带后台企业网站百度投放广告流程

扁平化企业网源码win8风格精简化源码asp带后台企业网站,百度投放广告流程,南宁制作网站的公司,wordpress 优秀站点一、原子操作简介 在多线程编程中,通常我们需要多个线程共享数据。但如果不加以控制,多个线程同时访问同一数据,可能会导致数据不一致或竞争条件(race conditions)。为了解决这个问题,我们引入了 原子操作…

一、原子操作简介

在多线程编程中,通常我们需要多个线程共享数据。但如果不加以控制,多个线程同时访问同一数据,可能会导致数据不一致或竞争条件(race conditions)。为了解决这个问题,我们引入了 原子操作

原子操作 是指在执行时不会被中断、打断或修改的操作。换句话说,原子操作要么完全执行,要么完全不执行,不会在中间被其他线程的操作干扰。这种特性对于多线程环境中的数据访问是至关重要的。

C++11 引入了 <atomic> 库,它提供了对原子操作的支持。通过这个库,我们可以确保在多线程环境中对数据的访问不会导致竞争条件。

二、C++ 原子操作的基本概念

在 C++ 中,原子操作的关键是 原子类型(atomic types)。原子类型是由 std::atomic 模板类提供的,它包装了一个类型,确保对该类型的所有操作(如读取、写入、更新等)都是原子的。

关键概念

  1. 原子类型 (std::atomic):可以包装任何基本类型(如 intboolfloat 等),并保证对这些类型的操作是原子的。
  2. 原子操作:对原子变量的操作是不可分割的,意味着在多线程中不会被打断。
  3. 原子变量:一个变量可以被声明为原子类型(如 std::atomic<int>),它保证在多线程环境下对该变量的操作是安全的。

常用的原子操作

  1. load:读取原子变量的值。
  2. store:将一个值存储到原子变量中。
  3. exchange:将原子变量的值替换为一个新值,并返回旧值。
  4. compare_exchange_weak / compare_exchange_strong:原子地进行条件交换操作。若当前值等于预期值,则交换新值。
  5. fetch_add / fetch_sub:原子地执行加法或减法操作,并返回旧值。

三、std::atomic 类模板

std::atomic 是一个模板类,可以用于包装基础数据类型,确保对该数据类型的所有操作都是原子性的。

基本使用

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
using namespace std;std::atomic<int> counter(0);void increment() {for (int i = 0; i < 100000; ++i) {counter.fetch_add(1, std::memory_order_relaxed);  // 原子递增}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 4; ++i) {threads.push_back(std::thread(increment));  // 启动4个线程}for (auto& t : threads) {t.join();  // 等待线程完成}cout << "Counter value: " << counter.load() << endl;  // 输出最终结果return 0;
}

常用原子操作的详细介绍

1. load() 和 store()

  • load():从原子变量读取值。
  • store():将值存储到原子变量中。
std::atomic<int> a(10);
int value = a.load();   // 获取原子变量a的值
a.store(20);            // 将原子变量a的值设置为20

2. exchange()

exchange 会将原子变量的值更新为给定的值,并返回原先的值。常用来在并发环境下进行“交换”操作。

std::atomic<int> a(5);
int old_value = a.exchange(10); // 将a的值设置为10,并返回旧值5

3. fetch_add() 和 fetch_sub()

  • fetch_add():执行原子加法操作,并返回旧值。
  • fetch_sub():执行原子减法操作,并返回旧值。
std::atomic<int> a(5);
int old_value = a.fetch_add(1);  // 将a加1,并返回旧值
// a的值现在是6

4. compare_exchange_weak() 和 compare_exchange_strong()

这两个函数会比较当前原子变量的值和预期值。如果相同,就将原子变量的值设置为新值。否则,操作失败并返回 false

  • compare_exchange_weak():若失败,可能会重新尝试,性能相对较高。
  • compare_exchange_strong():若失败,会返回 false,并且不再尝试。
std::atomic<int> a(5);
int expected = 5;
if (a.compare_exchange_weak(expected, 10)) {// 如果a的值是5,设置为10std::cout << "Value changed!" << std::endl;
} else {std::cout << "Value not changed!" << std::endl;
}

5. memory_order

std::atomic 的操作可以指定不同的内存顺序(memory ordering),控制不同线程之间的操作顺序。这对于高效并发编程非常重要。常见的内存顺序有:

  • memory_order_relaxed:不保证其他线程与该线程的操作顺序。
  • memory_order_consume:保证后续操作依赖于当前操作。
  • memory_order_acquire:保证所有的读取操作不会在当前操作之前执行。
  • memory_order_release:保证所有的写操作不会在当前操作之后执行。
  • memory_order_acq_rel:同时保证 acquire 和 release。
  • memory_order_seq_cst:最强的内存顺序,保证所有操作的顺序一致。

应用场景

  1. 计数器:
    原子计数器是一个典型的应用场景。多个线程可能会并发修改一个计数器,原子操作可以保证计数器值的一致性。

  2. 锁的实现:
    一些简化版的锁(如自旋锁)可以使用原子操作来减少性能开销。

  3. 无锁数据结构:
    通过原子操作可以设计一些高效的无锁数据结构(如无锁队列、栈等)。

  4. 状态标志:
    原子布尔值常常用来作为状态标志,例如线程是否完成,任务是否已开始等。

好的,以下是两道练习题目,可以帮助你更好地理解和掌握 C++ 中的原子操作和多线程编程。


四、练习题目

练习题 1: 使用 std::atomic 实现线程安全计数器

题目要求

  • 编写一个线程安全的计数器类,使用 std::atomic 来保证多个线程并发访问时的数据一致性。
  • 设计一个类 Counter,其中有一个原子整型成员变量 value
  • 提供 increment(增加)和 get_value(获取当前值)两个方法。
  • 创建多个线程并发地对计数器执行增加操作,最后输出计数器的最终值。

示例

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>using namespace std;class Counter {
private:std::atomic<int> value;public:Counter() : value(0) {}void increment() {value.fetch_add(1, std::memory_order_relaxed);}int get_value() const {return value.load(std::memory_order_relaxed);}
};void increment_counter(Counter& counter, int times) {for (int i = 0; i < times; ++i) {counter.increment();}
}int main() {Counter counter;// 创建多个线程并发增加计数器的值vector<thread> threads;for (int i = 0; i < 5; ++i) {threads.push_back(thread(increment_counter, ref(counter), 1000));}// 等待所有线程完成for (auto& t : threads) {t.join();}cout << "Final counter value: " << counter.get_value() << endl;return 0;
}

练习题 2: 使用 compare_exchange_strong 实现自旋锁

题目要求

  • 编写一个简单的自旋锁类,使用 std::atomic<bool> 来实现锁的原子操作。
  • 使用 compare_exchange_strong 来实现 lockunlock 操作。
  • main 函数中启动多个线程,模拟多个线程访问共享资源的场景,确保通过自旋锁控制资源的访问。

示例

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>using namespace std;class SpinLock {
private:std::atomic<bool> flag;public:SpinLock() : flag(false) {}void lock() {bool expected = false;while (!flag.compare_exchange_strong(expected, true)) {expected = false;  // 如果失败,重新尝试}}void unlock() {flag.store(false, std::memory_order_release);}
};SpinLock spinlock;void access_shared_resource(int thread_id) {spinlock.lock();cout << "Thread " << thread_id << " is accessing the shared resource." << endl;this_thread::sleep_for(chrono::milliseconds(100));  // 模拟处理过程cout << "Thread " << thread_id << " has finished accessing the resource." << endl;spinlock.unlock();
}int main() {vector<thread> threads;// 创建多个线程模拟并发访问共享资源for (int i = 0; i < 5; ++i) {threads.push_back(thread(access_shared_resource, i));}// 等待所有线程完成for (auto& t : threads) {t.join();}return 0;
}

总结

  1. 练习题 1 帮助你练习如何使用 std::atomic 实现线程安全的计数器,理解原子操作对共享数据的保护作用。
  2. 练习题 2 让你掌握如何使用 compare_exchange_strong 来实现自旋锁,理解锁机制的基本原理。

五、进一步讨论

为什么不使用原子操作会导致统计混乱:

假设有两个线程同时执行如下代码:

g_count = g_count + 1;

在没有原子操作的情况下,可能会发生以下步骤:

  1. 线程 A 读取 g_count 的值,假设为 5。
  2. 线程 B 也读取 g_count 的值,仍然是 5。
  3. 线程 A 对 g_count 进行加 1 操作,得到新的值 6。
  4. 线程 B 也对 g_count 进行加 1 操作,得到新的值 6。

最终的 g_count 值应该是 7(5 + 1 + 1),但实际上由于没有原子操作,线程 A 和线程 B 的加法操作是并行进行的,导致最终结果错误(g_count 被错误地更新为 6)。

解决方法: 使用原子操作,如 std::atomic 来确保这些操作是 原子的,即每次修改操作都是不可分割的,避免数据竞争。

支持的原子操作:

原子操作一般支持的操作包括:

  • 基本算术操作++--+=-=&=, |=, ^= 等。
  • 这些操作是原子的,也就是说,当多个线程同时对 std::atomic<int> 进行增减或位操作时,编译器会确保每次操作是不可分割的。

示例

#include <iostream>
#include <atomic>
#include <thread>
using namespace std;atomic<int> g_count = 0;void mythread1() {for (int i = 0; i < 1000000; i++) {g_count++;}
}int main() {std::thread t1(mythread1);std::thread t2(mythread1);t1.join();t2.join();cout << "正常情况下结果应该是2000000次,实际是" << g_count << endl;
}

不支持的操作:

并不是所有的操作都可以直接在原子变量上执行。例如,g_count = g_count + 1; 这种方式 不行,因为它首先会读取 g_count,然后执行加法并将结果写回 g_count,这个过程并不是原子操作。如果多个线程同时进行这种操作,可能会导致竞态条件,结果不正确。

为什么 g_count = g_count + 1; 不行:

  • g_count = g_count + 1; 这个表达式分为两步:

    1. 读取 g_count 的值。
    2. 将计算出的新值写回 g_count

    如果在这两步之间,另一个线程也读取并修改了 g_count 的值,最终可能导致结果错误。

  • 原子操作(如 fetch_addfetch_sub 等)可以保证这类操作是 不可分割 的,避免中间被打断。通过原子操作,线程会依次执行加法或减法操作,确保每次修改都正确地反映到内存中。

对自己进行操作:

一些原子类型(如 std::atomic<int>)只支持对自身进行修改的操作。这意味着,如果要执行某些复杂的表达式(例如 g_count = g_count + 1;),需要使用更复杂的原子操作(如 fetch_add),而不能直接使用加法或赋值操作符。

示例:

std::atomic<int> g_count(0);// 错误:不支持直接执行 g_count = g_count + 1;
g_count = g_count + 1;  // 编译错误// 正确:使用原子操作
g_count.fetch_add(1, std::memory_order_relaxed);  // 原子增加1
g_count++;
http://www.dtcms.com/wzjs/44457.html

相关文章:

  • 广西网站建设公司免费的个人主页网页制作网站
  • 响应式网站一般做多大微信朋友圈广告推广
  • 河南网站建设哪家有查询seo
  • 老司机ae86ug最新人口合肥百度关键词优化
  • 绵阳做手机网站建设焊工培训心得体会
  • 网页美工设计百度seo策略主要包括
  • 山西优化seo广州seo外包多少钱
  • 图片手机网站建设沧浪seo网站优化软件
  • 专业做淘宝网站推广吉林关键词排名优化软件
  • 网站开发用linux好吗网站推广哪家好
  • 系统之家网站怎么做郑州靠谱seo电话
  • 做彩票的网站有哪些制作一个网站需要多少费用
  • 西部数码空间可以做会所网站吗淘宝摄影培训推荐
  • 二手车网站制作b2b外链代发
  • 网站域名后缀的意思小红书推广方式有哪些
  • 网站淘客怎么做怎么设计网站
  • 深圳微网站建设公司佛山网络推广哪里好
  • 企业电子商务网站站长之家seo查询官方网站
  • 太原网站建设费用杭州做seo的公司
  • 手工做皮具国外的网站找推网
  • 网络宣传网站建设咨询怎么关闭seo综合查询
  • 长沙网站建设推广沧州网站建设优化公司
  • 重庆网站建设 公司恶意点击软件哪个好
  • php网站建设公司最新新闻热点素材
  • 寿光专业做网站的公司怎么推广自己的网站?
  • php怎么做网站程序品牌营销成功案例
  • 淘客选品网站开发市场调研分析报告
  • 域名注册好了怎么做网站重庆网络seo
  • 云空间的网站网站权重划分
  • 有什么兼职做it的网站好长沙seo袁飞