[c++]并发多线程概述

版权声明:转载请注明出处 https://blog.csdn.net/weixin_40937100/article/details/88760657

并发与多线程概述

基于c++11标准
想想轮子哥是怎么说的,反反复复把概念吃透,把知识留存在脑子里
不仅要听懂,练习、变现很重要

1.引言

1.可以给软件开两个线程,分别去处理不同的数据,提高效率。
2.开发多线程,是实力的体现,也是商用的需要。
3.线程开发有难度,理解上更难,需要一定的学习时间,但这恰恰也是实力的体现之处。所以高效突破这个点,可以灵活应用到不同的开发环境和语言环境当中,实现技术壁垒。
4. 放松心情,戒骄戒躁,不要急于求成,要稳住稳打。

2.并发的实现方法

  1. 多进程并发
  2. 单进程,多线程并发

3.多进程并发

  1. 进程之间的通讯。例如账号服务器和游戏逻辑服务器之间的通讯。实现方式包括管道,文件、消息队列,共享内容等(同一个电脑)
  2. 在不同的电脑上,可以socket通讯

4.多线程并发

  1. 每个线程都有自己的独立的运行路径,但是一个进程的所有线程共享地址空间(共享内存,比如全局变量在各个线程中都可以使用)
  2. 全局变量,引用,指针,都可以在线程之间传递。所以使用多线程开销远远小于多进程。
  3. 共享内存带来的新问题——数据一致性问题:线程A,B,可能在同时向同一个文件写数据,需要有一个先后问题。
  4. 优先考虑多线程,而不是多进程。(除非一定需要多进程)
  5. 侧重多线程并发的开发技术,后续谈到并发一般是默认多线程并发。

5.线程的优缺点

  1. 启动速度快,系统资源开销少,共享内存,轻量级。
  2. 使用起来有一定难度,数据处理的一致性问题要小心处理

6. c++11新标准线程库

  1. 以往的创建线程方法,都是操作系统提供一些多线程接口,如windows下包括CreateThread(),_beginthread(), _beginthreadexe()等;在Linux下包括pthread_create()创建线程。临界区,互斥量(不能跨平台编译)
  2. 线程库一般是跨平台的,windows和Linux系统只需要稍作配置,所以用起来不是很方便。
  3. 从c++11开始,c++本身增加了多线程执行;即,可以用c++11 这个语言来编写与平台无关的多线程程序,意味着可移植,减少开发人员工作量
    …不要懒惰和松懈,多敲…
  4. 若主线程执行完了,子线程会被进程强行终止。如果想保持子线程的运行状态,那么就需要主线程一直保持运行,不要在子线程结束前使主线程运行完毕。(有例外情况)
  5. 每一个子线程也需要有一个它的入口函数。

7.多线程案例——创建多线程程序

1.函数可调用对象创建线程

#include<thread>
//每个子线程都有自己的初始函数
void myprint(){
	cout<<"子线程1"<<endl;
	cout<<"子线程2"<<endl;
	cout<<"子线程3"<<endl;
	cout<<"子线程4"<<endl;
	cout<<"子线程5"<<endl;
	cout<<"子线程6"<<endl;
	cout<<"子线程7"<<endl;
	cout<<"子线程8"<<endl;
	//...
	//...
	cout<<"我的线程执行完毕"<<endl;
}
//创建一个thread类对象,也就创建了一条子线程,是和main同时运行的
int main(){

	/* thread join */
	thread threadObject(pyprint);	//thread是一个类,用以创建线程,构造函数的参数是一个入口函数名,也就是一个可调用对象,线程的入口是函数myprint,并且开始了子线程的执行
	threadObject.join();			//join函数阻塞主线程,让主线程等待子线程执行完毕后与其汇合,然后主线程再往下走					
									//上面的thread和join在调试时候的顺序,创建thread -> 监测myprint函数 -> 堵塞主线程,执行join -> 执行子线程的所有内容 -> 堵塞接触,执行主线程 -> 主线程结束.这是传统的多线程任务的写法,即为主线程等待子线程的运行完毕进行收尾
									//c++11可以打破传统多线程方法,主线程不必等待,下面介绍

	/* detach */ 
	//主线程和子线程分离,不再汇合,且主线程不再等待子线程
	//引入detach的原因在于,若令主线程依次等待所有的子线程全部执行完再收尾,不太合适
	//但是,最稳定的编程方法,还是传统的封锁机制
	//需要注释掉join调用
	threadObject.detach(); //使子线程threadObject与主线程失去关联,自己到系统后台运行
						   //相当于这个子线程被c++运行时库接管
						   //在该子线程执行完毕后,由运行运行时库负责清理内存空间和相关的资源
						   //这种进程称为守护线程(脱缰的野马)
						   //只要主线程结束,子线程后面的内容就不会输出出来

	/* joinable */
	//用以判断是否可以join或者detach,返回true或者false
	if(threadObject.joinable()){cout<<"ok"<<endl;}
	else{cout<<"no"<<endl;}
	//所以一般来讲,若想进行join封锁主线程,可先调用thread对象的joinable函数来检查是否可以join,
	//然后根据判断结果进行join操作,确保安全。
	


	cout<<"hello world1"<<endl;
	cout<<"hello world2"<<endl;
	cout<<"hello world3"<<endl;
	cout<<"hello world4"<<endl;
	cout<<"hello world5"<<endl;
	cout<<"hello world6"<<endl;
	cout<<"hello world7"<<endl;
	cout<<"hello world8"<<endl;
	return 0;
}

8.其他创建线程的方法

#include<thread>
//thread函数的构造函数中的参数是一个可调用对象,函数是可调用对象,类也是可调用对象
//下面演示用类创建多线程
class TA{
public:
	void operator()(){	//必须重载括号,不能待参数
		cout<<"我的线程开始执行了"<<endl;
		//...
		//...
		cout<<"我的线程结束执行了"<<endl;
	}
};

int main(){
	TA ta;
	thread Object(ta);
	Object.join();//令主线程等待子线程
	cout<<"hello world"<<endl;
}

9.创建多个线程

// Thread_1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<thread>
#include<map>
#include<vector>
#include<string>
#include<iostream>
#include<list>
#include<mutex>

using namespace std;

//线程入口函数
void myprint(int inum) {
	cout << "myprint线程开始执行,编号=" << inum << endl;
	//...
	cout << "线程结束" << endl;
	return;
}

int main()
{
	vector<thread> mythreads;//线程容器
	//希望创建10个线程,入口统一都是myprint
	for (int i = 0; i < 10; i++) {
		mythreads.push_back(thread(myprint, i)); //创建了10个线程,同时这10个线程,已经开始执行
	}

	for (auto iter = mythreads.begin(); iter != mythreads.end();iter++) {
		iter->join(); //等待10个线程都返回,就是说需要依次返回
	}

	cout << "I love China!" << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40937100/article/details/88760657