一、线程互斥
如果多个线程需要访问且可能修改同一个变量,那么需要加锁,保证同一时刻只有一个线程可以访问,这个动作即最小“原子操作”
方式1:
使用c++提供的类mutex,lock,unlock即可保证线程同步
#include <mutex>
mutex mx;
int haha()
{
mx.lock();
do()
  {
//
}
mx.unlock();
return ;
}

缺点:如果do函数异常,这里会造成死锁

方式2:使用lock_guard代替mutex

为了解决上述问题,使用lock_guard,这个模板类在构造函数里lock mutex,在析构函数里unlock mutex,所以不用自己在unlock

#include <mutex>
mutex mx;
int haha()
{
{//使用作用域,只需要锁住改变公共变量的代码
std::lock_guard<std::mutex> lck(m_mutex);
do()
  {
//
}
}
return ;
}

方式3:使用C++11原子操作

#include <atomic>
#include <thread>
#include <list>
std::atomic_int g_iCount = ; void threadfunc1()
{
while (g_iCount > )
{
printf("threadfunc1 g_iCount:%d\r\n", --g_iCount);
}
} void threadfunc2()
{
while (g_iCount > )
{
printf("threadfunc2 g_iCount:%d\r\n", --g_iCount);
}
} int main(int argc, char** argv)
{
thread t1(threadfunc1);
thread t2(threadfunc2); t1.join();
t2.join(); return ;
}
atomic_int 类型就是C++11提供的原子类型,还有很多其他类似的如bool、long、char等等,可以更高效的提供多线程安全访问,比线程锁更高效,因为它是汇编级别的。不过只有基本数据类型,其他数据结构和自定义类型就不行了

二、线程同步

线程同步是指不同线程按指定顺序做某事情,某一线程是否做某动作,是由另一个线程来管理的,如线程池按顺序调度队列,消费者会等待生产者生产了任务后再进行工作。如果不同步,我们一般会使用死循环来访问。这里举例生产-消费模型

生产者每隔t1时间会生产出一个任务,消费者选择最优方式去调度任务

方式1、不同步,使用死循环

std::deque<int> q;
std::mutex mu; void function_1()
{
int count = ;
while (count > )
{
std::unique_lock<std::mutex> locker(mu);
q.push_front(count);
locker.unlock();
std::this_thread::sleep_for(std::chrono::seconds());
count--;
}
} void function_2()
{
int data = ;
while (data != )
{
std::unique_lock<std::mutex> locker(mu);
if (!q.empty())
{
data = q.back();
q.pop_back();
locker.unlock();
std::cout << "t2 got a value from t1: " << data << std::endl;
}
else
     {
locker.unlock();
}
}
}
int main()
{
std::thread t1(function_1);
std::thread t2(function_2);
t1.join();
t2.join();
return ;
}
【代码来源:https://www.jianshu.com/p/c1dfa1d40f53】

这里消费者会死循环判断队列是否有数据,如果没有数据,也会死循环的跑,就会非常占cpu,此线程几乎会占满一个内核【单核计算机可能达到100%】,这种方式肯定不好。

有没有这样一种功能呢?如果队列没有任务,消费者就不占用cpu,释放内核,当生产者生产了任务后,唤醒消费者,消费者从队列里取数据,当任务做完,队列里没有任务后,继续释放内核...

看方式2:

C++11提供了条件变量,std::condition_variable

需要和互斥锁unique_lock一起使用。

继续看代码【代码来源:https://www.jianshu.com/p/c1dfa1d40f53】

std::deque<int> q;
std::mutex mu;
std::condition_variable cond; void function_1()
{
int count = ;
while (count > )
{
std::unique_lock<std::mutex> locker(mu);
q.push_front(count);
locker.unlock();
cond.notify_one(); // Notify one waiting thread, if there is one.
std::this_thread::sleep_for(std::chrono::seconds());
count--;
}
} void function_2()
{
int data = ;
while (data != )
{
std::unique_lock<std::mutex> locker(mu);
while (q.empty())
cond.wait(locker); // Unlock mu and wait to be notified
data = q.back();
q.pop_back();
locker.unlock();
std::cout << "t2 got a value from t1: " << data << std::endl;
}
}
int main()
{
std::thread t1(function_1);
std::thread t2(function_2);
t1.join();
t2.join();
return ;
}

核心:

①、在消费者里判断队列是否为空后,如果不为空则wait,等待生产者发送notify信号

②、在生产者那里,如果生产了任务,则发送notify信号,告诉消费者可以试图退出wait,判断队列是否为空,如果有任务则调度处理任务,如果还是空则说明此次notify是错误的,可能是其他地方发出来干扰的,生产者继续wait。

C++线程互斥、同步的更多相关文章

  1. C++ 11 线程的同步与互斥

    这次写的线程的同步与互斥,不依赖于任何系统,完全使用了C++11标准的新特性来写的,就连线程函数都用了C++11标准的lambda表达式. /* * thread_test.cpp * * Copyr ...

  2. 三十九、Linux 线程——线程的同步和互斥

    39.1 概念 线程同步 是一个宏观概念,在微观上包含线程的相互排斥和线程先后执行的约束问题 解决同步方式 条件变量 线程信号量 线程互斥 线程执行的相互排斥 解决互斥的方式 互斥锁 读写锁 线程信号 ...

  3. linux 线程的同步 一 (互斥量和信号量)

    互斥量(Mutex) 互斥量表现互斥现象的数据结构,也被当作二元信号灯.一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源. ...

  4. Python之多线程:线程互斥与线程同步

    一.锁在多线程中的使用:线程互斥 lock = threading.Lock()#创建一个锁对象 1.with lock: pass 和进程使用的方式相同   2.控制线程结束的时间 通过一个全局变量 ...

  5. Java并发编程,互斥同步和线程之间的协作

    互斥同步和线程之间的协作 互斥同步 Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLo ...

  6. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

  7. Java线程:线程的同步-同步方法

    Java线程:线程的同步-同步方法   线程的同步是保证多线程安全访问竞争资源的一种手段. 线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问 ...

  8. Java线程:线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...

  9. Java虚拟机13:互斥同步、锁优化及synchronized和volatile

    互斥同步 互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保证手段.同步是指子啊多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一 ...

随机推荐

  1. 【清北学堂】广州OI学习游记

    \(Day~0\) 早上\(9\)点多才爬起来,然后水了道题. 下午从[数据删除]出发,颠簸了将近\(5\)个小时终于抵达广州. 一出地铁站--卧槽这天,卧槽这风,要下雨的节奏? 没过两分钟倾盆大雨. ...

  2. python 杂记-unittest

    介绍单元测试的好文:https://mp.weixin.qq.com/s/njxc8GXSlc3z_RibK70ROg setUpModule/tearDownModule:在整个模块的开始和结束时被 ...

  3. C语言Ⅰ博客作业06

    这个作业属于哪个课程 C语言程序设计Ⅰ 这个作业要求在哪里 熟练掌握多分支结构,字符型数据类型和逻辑运算符 我在这个课程的目标是 https://www.cnblogs.com/tongyingjun ...

  4. [Cypress] install, configure, and script Cypress for JavaScript web applications -- part5

    Use the Most Robust Selector for Cypress Tests Which selectors your choose for your tests matter, a ...

  5. 第四章 初始CSS

    一.引入样式 1.行内样式表 <h1 style="color: red;font-size: 18px;">10-30</h1> 2.内部样式表(在hea ...

  6. win32按钮

    1.按钮是什么 在win32窗口中,经常可以看到按钮,点击按钮可以触发各种事件:   创建按钮的函数: void CreateButton(HWND hwnd) //参数为父窗口句柄,按钮必须属于一个 ...

  7. linux中的set ff=unix

    set ff=unix : 告诉 vi 编辑器,使用unix换行符. 操作步骤: 1.用vi命令打开文件 2.直接输入 :set ff=unix

  8. Elasticsearch 使用:创建、插入、查询、更新、删除

    Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上. Lucene 可能是目前存在的,不论开源还是私有的,拥有最先进,高性能和全功能搜索 ...

  9. java+上传一个文件夹

    在web项目中上传文件夹现在已经成为了一个主流的需求.在OA,或者企业ERP系统中都有类似的需求.上传文件夹并且保留层级结构能够对用户行成很好的引导,用户使用起来也更方便.能够提供更高级的应用支撑. ...

  10. nginx的跨域设置

    官方文档 中说,只有当响应状态码为以下几种类型中之一时,add_header 才会生效.如果需要 add_header 在所有情况下都生效,可以在后面加上 always 参数即可解决. Adds th ...