多线程01
一、并行和并发:
并行:多个任务在同一时刻同时执行。
并发:多个任务顺序执行,不是同时。
二、进程和线程:
进程是系统分配资源的最小单位,线程是cpu执行任务的最小单位。
对于单核CPU而言,同一时刻只能执行一个线程。每隔一定时间会切换线程(可能是同一个进程的线程,也可能是另外一个进程的线程,如果是其它进程的线程)。
在单核CPU上实现的多线程其实是“假”的多线程,CPU同一时刻只能执行,只不过CPU切换任务的速度很快,所以你感觉是同时执行了多个任务,即实现了多线程。
在单核CPU上,无论是线程还是进程,都只能并发执行,多核CUP上有可能实现并行执行。
三、多线程
下面用一个最简单的多线程例子说明。
C++11之前创建多线程的函数有2个:_beginthreadex、CreateThread
前者是C++库函数,后者是Windows API。我们使用_beginthreadex。
int main() { for (int i = 0; i < 5; i++) { cout<<"1234567890------"<< i<<endl; } system("pause"); return 0; }
上述程序只有一个进程,mian函数就是这个进程的主线程,打印结果,是顺序执行,顺序打印。
现在给它添加一个子线程:
//线程函数 unsigned __stdcall ThreadProc( void * lpParameter) { int* p = (int*)lpParameter; for (int i = 0; i < *p; i++) { cout<<"abcdefghij------"<< i<<endl; } return 0; } int main() { unsigned nThreadID = 0 ; int n = 5; HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID ); CloseHandle(hThread); for (int i = 0; i < 5; i++) { cout<<"1234567890------"<< i<<endl; } system("pause"); return 0; }
打印结果如下。可以看到,虽然代码中,主线程的打印部分是写在子线程的后面的,但结果是主线程的打印内容先出来了。
我们再在主线程和子线程中分别加上一个Sleep,如下:
//线程函数 unsigned __stdcall ThreadProc( void * lpParameter) { int* p = (int*)lpParameter; for (int i = 0; i < *p; i++) { Sleep(10); cout<<"abcdefghij------"<< i<<endl; } return 0; } int main() { unsigned nThreadID = 0 ; int n = 5; HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID ); CloseHandle(hThread); for (int i = 0; i < 5; i++) { Sleep(10); cout<<"1234567890------"<< i<<endl; } system("pause"); return 0; }
打印结果如下。可以发现顺序又变了,而且相互影响,没有规律。
四、线程安全
上面的最后一个例子,可以发现最后的打印结果没有规律,不可预见,就称之为线程不安全,在实际项目中这样简单地使用多线程代码肯定是不行的。
线程不安全的原因通常是共享了资源,解决方法是使用加锁来规避。
锁的核心功能是多个线程访问同一个资源,保证同一时刻只有一个线程能使用该资源,对该资源有独占访问权。
C++ 11 封装了类mutex,帮我们完成,直接用就行了。
五、使用锁mutex
mutex mutex1 ; unsigned __stdcall ThreadProc( void * lpParameter) { int* p = (int*)lpParameter; for (int i = 0; i < *p; i++) { mutex1.lock(); Sleep(10); cout<<"abcdefghij------"<< i<<endl; mutex1.unlock(); } return 0; } int main() { unsigned nThreadID = 0 ; int n = 5; HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID ); CloseHandle(hThread); for (int i = 0; i < 5; i++) { mutex1.lock(); Sleep(10); cout<<"1234567890------"<< i<<endl; mutex1.unlock(); } system("pause"); return 0; }
结果如下:
其实 最后一行 system("pause"); 也使用了cout,完整的写法应该在它的前后也加上锁。
否则,如果将Sleep的参数改大,比如我的电脑上,将其改为Sleep(5000),可能出现如下结果:
六、使用C++11开发多线程
C++11不仅仅有线程锁的封装,也提供了线程的封装 std::thread。
在C++11之前,我们只能使用windows的api来实现多线程,现在抛弃前面创建多线程的那一套吧,直接使用封装好的 std::thread 类。
原文地址:https://www.cnblogs.com/hosseini/p/15162745.html
- github搭建个人网站
- Android:一个高效的UI才是一个拉风的UI
- 什么是ORM?为什么用ORM?浅析ORM的使用及利弊
- .NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法
- Android中图片大小和屏幕密度的关系讲解
- C# WINFORM通过委托和事件窗体间(跨窗体)传值(自定义事件参数)--实例详解
- Apache Spark 2.0预览:机器学习模型持久性
- 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler
- 携程Android App的插件化和动态加载框架
- Spring Boot构建RESTful API与单元测试
- Volley解析之表单提交篇
- JAVA中重写equals()方法的同时要重写hashcode()方法
- 调用CodeSmith类库实现代码生成(含源码)
- 1分钟生成Net对象的注释
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux 创建修改删除用户和组的方法
- 详解Ubuntu16.04安装nvidia驱动+CUDA+cuDNN的教程
- linux free命令详解
- Linux下memcache编译安装与基本使用教程
- Linux中MongoDB如何实现远程自动备份详解
- 覆盖原先的PATH导致命令失效提示command not found的解决方法
- Linux中可以节省你时间的15个命令别名
- Linux下利用unzip命令如何解压多个文件详解
- linux查看磁盘使用情况方法
- 详解Linux(centos7)下安装OpenSSL安装图文方法
- 面试官:考你几个简单的事件问题吧
- 如何创建Linux的swap交换分区文件的方法步骤
- Linux利用keepalived实现lvs的高可用性的操作方法
- Canvas系列(11):动画中级
- 使用autoconf生成Makefile并编译工程的步骤