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

三网合一网站建设全包费用百度站长平台app

三网合一网站建设全包费用,百度站长平台app,建个免费的销售网站,一分钟用自己的电脑做网站文章目录thread 创建mutexmutexrecursive_mutextimed_mutexlock_guard原子操作atomic条件变量condition_variable其他线程安全问题shared_ptr单例模式C 线程库是 C11 标准中引入的一个特性,它使得 C 在语言级别上支持多线程编程,不需要依赖第三方库或操作…

文章目录

  • thread 创建
  • mutex
    • mutex
    • recursive_mutex
    • timed_mutex
    • lock_guard
  • 原子操作
    • atomic
  • 条件变量
    • condition_variable
  • 其他线程安全问题
    • shared_ptr
    • 单例模式

C++ 线程库是 C++11 标准中引入的一个特性,它使得 C++ 在语言级别上支持多线程编程,不需要依赖第三方库或操作系统 API。C++ 线程库主要包括以下几个部分:

  • std::thread:创建和管理子线程的类
  • std::mutex:互斥锁,用于保护共享数据的访问
  • std::condition_variable:条件变量,用于同步线程之间的状态
  • std::futurestd::promise:异步任务和结果的封装
  • std::async:启动异步任务的函数

thread 创建

thread

std::thread 类的构造函数:

img

std::thread 是一个类,它表示一个可执行的线程。你可以用它来创建和管理子线程,让它们并发地执行不同的任务。std::thread 有以下几个特点:

  • 它不可拷贝,只能移动。如上(3)(4)
  • 它可以被 join 或 detach,join 表示等待线程结束,detach 表示让线程自行运行
  • 它有一个唯一的标识符 std::thread::id,可以用来区分不同的线程

img

在命名空间 this_thread下有一个 get_id 函数,可以帮助我们获取线程 id

yield:可以使当前线程让出时间片

sleep_until:sleep 到特定时间点

sleep_for:sleep 一段时间


一个双线程循环打印的例子:

#include <iostream>
#include <thread>using namespace std;void print(int n) {for (int i = 0; i < n; ++i) {cout << this_thread::get_id() << ":" << i << endl; // 打印 [线程id]:ithis_thread::sleep_for(chrono::seconds(1)); // sleep 1 秒}
}int main() {thread t1(print, 100);thread t2(print, 100);t1.join();t2.join();return 0;
}

mutex

mutex

例一:给上面的代码加锁

#include <iostream>
#include <thread>
#include <mutex>using namespace std;mutex mtx; // 创建一把锁void print(int n) {mtx.lock();	// 加锁for (int i = 0; i < n; ++i) {cout << this_thread::get_id() << ":" << i << endl; // 打印 [线程id]:ithis_thread::sleep_for(chrono::milliseconds(100)); // sleep 100 ms}mtx.unlock(); // 解锁
}int main() {thread t1(print, 100);thread t2(print, 100);t1.join();t2.join();return 0;
}

例二:

如果锁是创建在局部,则要通过函数参数传入

由于锁不支持拷贝,所以必须通过引用传入,又由于可变模板参数会默认识别成传值,所以必须先使用 ref 来强制转换成引用。

#include <iostream>
#include <thread>
#include <mutex>using namespace std;void print(int n, mutex& mtx) {mtx.lock();	// 加锁for (int i = 0; i < n; ++i) {cout << this_thread::get_id() << ":" << i << endl; // 打印 [线程id]:ithis_thread::sleep_for(chrono::milliseconds(100)); // sleep 100 ms}mtx.unlock(); // 解锁
}int main() {mutex mtx; // 创建一把锁thread t1(print, 100, ref(mtx)); // 必须使用 ref 来传引用thread t2(print, 100, ref(mtx));t1.join();t2.join();return 0;
}

例三:

配合 vectorlambda 使用

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>using namespace std;int main() {mutex mtx; // 创建一把锁int x = 0, n = 10, m;cin >> m;vector<thread> v(m);for (int i = 0; i < m; ++i) {v[i] = thread([&]() {mtx.lock();	// 加锁for (int i = 0; i < n; ++i) {cout << this_thread::get_id() << ":" << i << endl; // 打印 [线程id]:ithis_thread::sleep_for(chrono::milliseconds(100)); // sleep 100 ms}mtx.unlock(); // 解锁});}for (auto& t : v) {t.join();}return 0;
}

recursive_mutex

如果在递归函数中使用普通的互斥锁会造成死锁,因为当一个线程执行到 unlock 之前就可能会递归调用自己,然后从头开始执行,当再次遇到 lock 时,由于之前没有执行 unlock,就会死锁。

recursive_mutex 可以防止递归造成的死锁,它在每次 lock 的时候会判断是不是当前线程,如果是,就不用阻塞,可以继续向下执行了。

timed_mutex

定时互斥锁,该类锁可以设置锁住的时间。

由它的两个成员实现 try_lock_for 和 try_lock_until.

到时间后我们不需要手动解锁,而是自动解锁。

lock_guard

智能锁,通过 RAII 技术实现。

它的实现类似于如下方法:

template<class Lock>
class LockGuard {
public:LockGuard(Lock& lk) : _lock(lk) {_lock.lock();}~LockGuard() {_lock.unlock();}
private:Lock& _lock;
};

例:

int main() {mutex mtx; // 创建一把锁atomic<int> x = 0;//int x = 0;int n = 100, m;cin >> m;vector<thread> v(m);for (int i = 0; i < m; ++i) {v[i] = thread([&]() {for (int i = 0; i < n; ++i) {lock_guard<mutex> lk(mtx); // 智能锁cout << this_thread::get_id() << ':' << i << endl;this_thread::sleep_for(chrono::microseconds(100));}});}for (auto& t : v) {t.join();}cout << x << endl;return 0;
}

注:unique_guardlock_guard 多一个支持手动加锁的解锁的功能。

原子操作

atomic

我们知道,++ 操作不是线程安全的,要避免多线程同时修改造成的数据不一致的问题,可以通过加锁来解决。

但是互斥锁会产生上下文切换的开销,容易产生死锁。更好的解决方法是使用原子操作。


CAS (Compare & Set,或 Compare & Swap)是一种原子操作,也就是说它是一个不会被其他线程打断的操作。CAS 有三个操作数:内存值 V,旧的预期值 A,要修改的新值 B。CAS 的过程是这样的:先比较内存值 V 和旧的预期值 A 是否相等,如果相等,就用新值 B 替换内存值 V;如果不相等,就放弃操作或者重试。CAS 可以用于实现无锁算法,避免多线程同时修改同一数据时产生的数据不一致问题。

C++11 中的 atomic 类就是 CAS 的一种实现。

例:

#include <iostream>
#include <thread>
#include <vector>using namespace std;int main() {//int x = 0;atomic<int> x = 0;int n = 100000, m;cin >> m;vector<thread> v(m);for (int i = 0; i < m; ++i) {v[i] = thread([&]() {for (int i = 0; i < n; ++i) {++x;				// 原子操作}});}for (auto& t : v) {t.join();}cout << x << endl;return 0;
}

条件变量

condition_variable

条件变量是一种同步原语,用于让线程在某个条件发生时才继续执行。 条件变量与互斥锁配合使用,让线程在等待条件时释放锁,从而避免竞争状态。条件变量提供了一种原子操作,即解锁并睡眠的操作,以及唤醒并加锁的操作。条件变量有两个动作:等待(wait)和通知(notify)。等待动作会让线程挂起,并释放已经持有的锁。通知动作会唤醒一个或多个等待的线程,并让它们重新获取锁。

在 C++11 中,你可以使用 std::condition_variable 类来创建和操作条件变量。你需要配合一个互斥锁(std::mutex)和一个谓词(std::function<bool()>)来使用条件变量。你可以调用条件变量的成员函数,如wait()notify_one()notify_all()等来实现线程间的同步。


img

predicate(2) 是增加了谓词的版本,pred 是一个 std::function<bool()>,它返回 false 则表示阻塞,返回 true 时解除阻塞。

:创建两个线程,交替打印 0~100,如线程 t2 先打印 0,则下面必须是另一个线程 t1 打印 1,然后 t2 打印 2 …

这是一个运用条件变量解决的经典场景,我们可以让一个线程打印完一个数之后立马通知另一个线程,从而保证两个线程交替进行。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>using namespace std;int main() {int i = 0;int n = 100;mutex mtx;condition_variable cv;bool ready = true; // 开始标志,初始为 true,可以使线程 t2 先打印thread t1([&]() {while (i < n) {unique_lock<mutex> lock(mtx); // 条件变量需要的互斥锁cv.wait(lock, [&ready]() {return !ready; }); // 如果 ready 为 true 则阻塞cout << this_thread::get_id() << ":" << i << endl;++i;ready = true;	// 更改条件cv.notify_one();// 唤醒另一个线程}});thread t2([&]() {while (i < n) {unique_lock<mutex> lock(mtx);cv.wait(lock, [&ready]() {return ready; }); // 如果 ready 为 false 则阻塞cout << this_thread::get_id() << ":" << i << endl;++i;ready = false;	// 更改条件cv.notify_one();// 唤醒另一个线程}});t1.join();t2.join();return 0;
}

例2:生产者消费者模型

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>std::mutex mtx; // 互斥锁
std::condition_variable cv; // 条件变量
std::queue<int> q; // 共享队列
bool finished = false; // 结束标志void producer(int n) {for (int i = 0; i < n; ++i) {std::unique_lock<std::mutex> lock(mtx); // 加锁std::this_thread::sleep_for(std::chrono::milliseconds(100));q.push(i); // 生产数据std::cout << "produced " << i << "\n";lock.unlock(); // 解锁cv.notify_one(); // 通知消费者}{std::unique_lock<std::mutex> lock(mtx); // 加锁finished = true; // 设置结束标志}cv.notify_all(); // 通知所有消费者
}void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx); // 加锁cv.wait(lock, [] {return finished || !q.empty(); }); // 等待条件成立:结束或队列非空if (finished && q.empty()) {break; // 结束且队列空,退出循环}std::this_thread::sleep_for(std::chrono::milliseconds(100));int x = q.front(); // 取出数据q.pop();std::cout << std::this_thread::get_id() << ": consumed " << x << "\n";}
}int main() {std::thread t1(producer, 10); // 创建生产者线程,生产10个数据std::thread t2(consumer); // 创建消费者线程1std::thread t3(consumer); // 创建消费者线程2t1.join();t2.join();t3.join();return 0;
}

运行结果

img

其他线程安全问题

shared_ptr

Q:shared_ptr 是线程安全的吗?

A:shared_ptr 的引用计数保证是线程安全的,但访问资源不是,如果你想在多个线程之间共享同一个 shared_ptr 指向的对象,你需要使用互斥锁或原子操作来保护它。


单例模式

饿汉模式:

  • 特点:
    1. 不允许随便创建对象,构造函数私有
    2. 防拷贝
    3. main 函数之前就创建初始化对象
  • 缺点:
    1. 对象初始化麻烦,影响程序启动速度
    2. 多个单例类,如果有依赖顺序关系,无法控制

懒汉模式:

  • 特点:
    1. 不允许随便创建对象,构造函数私有
    2. 防拷贝
    3. 第一次使用对象时,创建对象
  • 缺点:
    1. 多个线程使用,第一次调用存在竞争的线程安全问题。

所以饿汉模式不用考虑线程安全问题,而懒汉模式有线程安全问题

下面是一个懒汉模式的代码示例:

class Singleton {
public:static Singleton* GetInstance() {if (_spInst == nullptr) {_spInst = new Singleton;}return _spInst;}
private:Singleton() {}Singleton(const Singleton&) = delete;static Singleton* _spInst;
};Singleton* Singleton::_spInst = nullptr;

如果有多个线程同时调用 GetInstance,可能会创建多个对象实例,违反了单例模式的原则。

这个问题可以通过加锁解决

下面的代码是一个双重检查加锁的懒汉模式的实现,双重检查加锁是一种用于减少获取锁的开销的软件设计模式。程序先检查锁定条件,只有当检查表明需要锁定时才获取锁,然后检查是否已经实例化对象,防止创建多个对象。

class Singleton {
public:static Singleton* GetInstance() {if (_spInst == nullptr) { // 第一重检查std::unique_lock<std::mutex> lock(_mtx);if (_spInst == nullptr) { // 第二重检查_spInst = new Singleton;}}return _spInst;}
private:Singleton() {}Singleton(const Singleton&) = delete;static Singleton* _spInst;static std::mutex _mtx;
};Singleton* Singleton::_spInst = nullptr;
std::mutex Singleton::_mtx;

你会发现这两重检查缺一不可,如果缺少了第一重检查,那么每次调用 GetInstance 都会加锁解锁,增加了获取锁的开销。如果缺少了第二重检查,那么两个线程会竞争一把锁,当其中一个线程加锁之后,另一个线程会阻塞,当一个线程完成了对象的实例化,并释放锁,另一个线程就会获取锁,此时如果没有第二重检查,那么它就会再创建一个对象,违反了单例模式的原则。


第二种线程安全的懒汉模式的实现方式:

它利用了静态局部对象只会在第一次调用时初始化特性。

class Singleton {
public:static Singleton* GetInstance() {static Singleton _s;return &_s;}
private:Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(Singleton const&) = delete;
};

但是这种实现方式有一个缺点,它在 C++11 之前不能保证是线程安全的,因为 C++11 之前局部静态对象的构造函数并不能保证是线程安全的。

http://www.dinnco.com/news/23325.html

相关文章:

  • 什么是电子商务网站开发最近10个新闻
  • 南京做网站建设的公司哪家好推销网站
  • 一家专业做家谱的网站关键词简谱
  • 凡客建站官网登录入口什么是市场营销
  • 做北京塞车网站石家庄最新消息今天
  • 网站个人建设seo优化服务商
  • 免费网站教程视频外链平台
  • 什么查网站是否降权网络推广公司联系方式
  • 成都本地网站百度人工智能
  • 零售网站建设属于网络营销特点的是
  • 2022年中央经济工作会议网站seo推广优化教程
  • 科技 响应式网站模板下载网络推广主要工作内容
  • 网站建设教程 零基础类似58的推广平台有哪些平台
  • 用php建设一个简单的网站关键词搜索方法
  • 变更股东怎样在工商网站做公示教育培训网站模板
  • 免费用手机制作网站 百度百成都seo顾问
  • 山东省建设执业官方网站上海优化网站公司哪家好
  • 做网站 怎么提升浏览量邯郸seo优化
  • 象58同城网站建设需要多少钱在线域名查询网站
  • 自己做的网站视频播放不了友链
  • 网站建设框架程序环球网疫情最新动态
  • 网站安全建设进展情况汇报国际局势最新消息今天
  • 上饶做网站公司百度云资源搜索引擎
  • php在网站制作中的运行机制网站制作河南
  • 广州网站建设公司乐云seo598郑州优化公司有哪些
  • 网站备案号含义关键词优化推广排名软件
  • 炫酷网站设计百度地图导航2021最新版
  • 苏州网站制作简述如何对网站进行推广
  • 哈尔滨做网站的oeminc网上开店如何推广自己的网店
  • 网站建设功能要求广州中小企业seo推广运营