热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++多线程编程(1)

    对于单处理器系统,处理器咋一个单元时间内只能执行一个进程,操作系统系统以极快的速度在多个线程之间进行切换,营造了一种多个进程同时运行的假象。1.一些基本概念:c++中的静态

       对于单处理器系统,处理器咋一个单元时间内只能执行一个进程,操作系统系统以极快的速度在多个线程之间进行切换,营造了一种多个进程同时运行的假象。


1. 一些基本概念:

c++中的静态库与动态库:

1. 静态库:*.lib 是指一些已经编译过的代码,在程序运行之前,静态库在编译的时候被放入到可执行文件中。

     静态库在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,对应的链接方式称为静态链接。静态库与汇编生成的目标文件(.o文件)一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的归档集合,即很多目标文件经过压缩打包后形成的一个文件。

静态库有两个重大缺点:

    1)空间浪费

    2)静态链接对程序的更新、部署和发布会带来很多麻烦。一旦程序中有任何模块更新,整个程序就要重新链接,发布给用户。

2. 动态库:*.dll 与静态库不同的是,动态库在程序开始执行后才开始进行链接,可以将许多程序都会用到的函数放入到动态库中。在这样就不必在每个程序中都包含这些函数了,只需在运行时链接一个动态库就可以了。

    动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。

clip_image023[4]

一般推荐使用lock_guard()来替代lock/unlock,因为更加安全。lock_guard在构造时会自动锁定互斥量,在退出作用后进行析构时就会自动解锁互斥量,避免忘记unlock操作。

lock_guard用到了RAII技术:

使用局部对象管理资源的技术通常称为“资源获取就是初始化”,即Resource Acquisition Is Initialization 机制。这一机制是Bjarne Stroustrup首先提出的,要解决的是这样一个问题:

          在C++中,如果在这个程序段结束时需要完成一些资源释放的工作,那么正常情况下自然是没有什么问题,但是当一个异常抛出时,释放资源的语句就不会被执行。于是Bjarne Stroustrup就想到确保 能运行资源释放代码的地方就是在这个程序段(栈帧)中放置的对象的析构函数了,因为stack winding会保证它们的析构函数都会被执行。将初始化和资源释放都放到一个包装类中的好处:

         a. 保证了资源的正常释放

         b. 省去了在异常处理中冗长而重复甚至有些还不一定执行到的清理逻辑,进而确保了代码的异常安全。

         c. 简化代码体积。

所在lock_guard在类的构造函数中和会分配资源,在析构函数中释放资源,保证资源在出了作用域之后就释放,上面的例子使用lock_guard后会更加简洁:


// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
mutex g_lock; // 独占互斥量
void func()
{
std::lock_guard locker(g_lock);
cout <<"Enter thread " < std::this_thread::sleep_for(std::chrono::seconds(2));
cout <<"Leaving thread " <}
int main()
{
thread t1(func);
thread t2(func);
thread t3(func);
t1.join();
t2.join();
t3.join();
return 0;
}

2. std::recursive_mutex:  递归互斥量,不带超时功能。

递归锁允许同一线程多次获得该互斥锁,可以解决同一线程需要多次获取互斥量时的死锁的问题,一个线程多次获取同一互斥量时会发生死锁问题:

例如:


// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
struct Complex
{
std::mutex mutex;
int i;
Complex() : i(2) {}; // 构造函数
void mul(int x)
{
std::lock_guard lock(mutex); // 获取互斥量
i *= x;
}
void div(int y)
{
std::lock_guard lock(mutex); // 获取互斥量
i /= y;
}
void both(int x, int y)
{
std::lock_guard lock(mutex); // 获取互斥量
mul(x);
div(y);
}
};
int main()
{
Complex cmp;
cmp.both(21, 2);
cout < return 0;
}

在主线程中多次获取互斥量,就会发生死锁。因为互斥量已被当前线程获取,无法释放,就会导致这样的问题。

递归锁可以解决这种问题:


// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
struct Complex
{
std::recursive_mutex mutex;
int i;
Complex() : i(2) {}; // 构造函数
void mul(int x)
{
std::lock_guard lock(mutex); // 获取互斥量
i *= x;
}
void div(int y)
{
std::lock_guard lock(mutex); // 获取互斥量
i /= y;
}
void both(int x, int y)
{
std::lock_guard lock(mutex); // 获取互斥量
mul(x);
div(y);
}
};
int main()
{
Complex cmp;
cmp.both(21, 2);
cout < return 0;
}

但是递归锁会存在如下的缺点:

a. 用到递归锁的多线程互斥处理本身是可以简化的。递归互斥量很容易产生复杂的逻辑,会导致线程同步引起的晦涩的问题。

b. 与非递归锁相比,递归锁的效率会更低。

c. 递归锁没有说明一个线程最多可以重复获得几次互斥量,一旦超过一定的次数,再调用lock就会抛出std::system的错误。

3. std::timed_mutex:   带超时的互斥量,不能递归使用。

timed_mutex在获取锁时增加超时等待功能,因为有时候不知道获取锁需要等待多久,为了不至于一直在等待获取互斥量,可以设置一个超时时间,在超时后还可以做其他的事情。多出的两个接口为:try_lock_for, try_lock_until.

例如:


// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
void work()
{
std::timed_mutex mutex;
// 定义函数
std::chrono::milliseconds timeout(100); // 100ms
while (true)
{
if (mutex.try_lock_for(timeout)) // 获取到互斥量
{
cout <<"Thread " < std::chrono::milliseconds sleep_time(250);
this_thread::sleep_for(sleep_time);
mutex.unlock(); // 释放互斥量
this_thread::sleep_for(sleep_time);
}
else // 未获取到互斥量 处理其他事务
{
cout <<"Thread " < std::chrono::milliseconds sleep_time(100);
this_thread::sleep_for(sleep_time);
}
}
}
int main()
{
thread t1(work);
thread t2(work);
t1.join();
t2.join();
return 0;
}

------------------------------------------------------------分割线------------------------------------------------------------------



推荐阅读
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了Android中的assets目录和raw目录的共同点和区别,包括获取资源的方法、目录结构的限制以及列出资源的能力。同时,还解释了raw目录中资源文件生成的ID,并说明了这些目录的使用方法。 ... [详细]
author-avatar
洪泽湖沟鼠_203
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有