c++的并发操做(多线程)

C++11标准在标准库中为多线程提供了组件,这意味着使用C++编写与平台无关的多线程程序成为可能,而C++程序的可移植性也获得了有力的保证。另外,并发编程可提升应用的性能,这对对性能锱铢必较的C++程序员来讲是值得关注的。ios

1. 何为并发

并发指的是两个或多个独立的活动在同一时段内发生。生活中并发的例子并很多,例如在跑步的时候你可能同时在听音乐;在看电脑显示器的同时你的手指在敲击键盘。这时咱们称咱们大脑并发地处理这些事件,只不过咱们大脑的处理是有次重点的:有时候你会更关注你呼吸的频率,而有时候你更多地被美妙的音乐旋律所吸引。这时咱们能够说大脑是一种并发设计的结构。这种次重点在计算机程序设计中,体现为某一个时刻只能处理一个操做。程序员

与并发相近的另外一个概念是并行。它们二者存在很大的差异。并行就是同时执行,计算机在同一时刻,在某个时间点上处理两个或以上的操做。判断一个程序是否并行执行,只须要看某个时刻上是否多两个或以上的工做单位在运行。一个程序若是是单线程的,那么它没法并行地运行。利用多线程与多进程可使得计算机并行地处理程序(固然 ,前提是该计算机有多个处理核心)。编程

  • 并发:同一时间段内能够交替处理多个操做:

图中整个安检系统是一个并发设计的结构。两个安检队列队首的人竞争这一个安检窗口,两个队列可能约定交替着进行安检,也多是你们同时竞争安检窗口(通讯)。后一种方式可能引发冲突:由于没法同时进行两个安检操做。在逻辑上看来,这个安检窗口是同时处理这两个队列。promise

  • 并行:同一时刻内同时处理多个操做:

图中整个安检系统是一个并行的系统。在这里,每一个队列都有本身的安检窗口,两个队列中间没有竞争关系,队列中的某个排队者只需等待队列前面的人安检完成,而后再轮到本身安检。在物理上,安检窗口同时处理这两个队列。安全

并发的程序设计,提供了一种方式让咱们可以设计出一种方案将问题(非必须地)并行地解决。若是咱们将程序的结构设计为能够并发执行的,那么在支持并行的机器上,咱们能够将程序并行地执行。所以,并发重点指的是程序的设计结构,而并行指的是程序运行的状态。并发编程,是一种将一个程序分解成小片断独立执行的程序设计方法。多线程

2.并发的基本方式途径

多线程与多进程是并发的两种途径。
想象两个场景:并发

  • 场景一:你和小伙伴要开发一个项目,但小伙伴们放寒假都回家了,大家只能经过QQ聊天、手机通话、发送思惟导图等方式来进行交流,总之大家没法很方便地进行沟通。好处是大家各自工做时能够互不打扰。
  • 场景二:你和小伙伴放假都呆在学校实验室中开发项目,大家能够聚在一块儿使用头脑风暴,可使用白板进行观点的阐述,总之大家沟通变得更方便有效了。有点遗憾的是你在思考时可能有小伙伴过来问你问题,你受到了打扰。

这两个场景描绘了并发的两种基本途径。每一个小伙伴表明一个线程,工做地点表明一个处理器。场景一中每一个小伙伴是一个单线程的进程,他们拥有独立的处理器,多个进程同时执行;场景二中只有一个处理器,全部小伙伴都是属于同一进程的线程。ide

2.1 多进程并发

多个进程独立地运行,它们之间经过进程间常规的通讯渠道传递讯息(信号,套接字,文件,管道等),这种进程间通讯不是设置复杂就是速度慢,这是由于为了不一个进程去修改另外一个进程,操做系统在进程间提供了必定的保护措施,固然,这也使得编写安全的并发代码更容易。
运行多个进程也须要固定的开销:进程的启动时间,进程管理的资源消耗。函数

2.2 多线程并发

在当个进程中运行多个线程也能够并发。线程就像轻量级的进程,每一个线程相互独立运行,但它们共享地址空间,全部线程访问到的大部分数据如指针、对象引用或其余数据能够在线程之间进行传递,它们均可以访问全局变量。进程之间一般共享内存,但这种共享一般难以创建且难以管理,缺乏线程间数据的保护。所以,在多线程编程中,咱们必须确保每一个线程锁访问到的数据是一致的。性能

3. C++中的并发与多线程

C++标准并无提供对多进程并发的原生支持,因此C++的多进程并发要靠其余API——这须要依赖相关平台。
C++11 标准提供了一个新的线程库,内容包括了管理线程、保护共享数据、线程间的同步操做、低级原子操做等各类类。标准极大地提升了程序的可移植性,之前的多线程依赖于具体的平台,而如今有了统一的接口进行实现。

C++11 新标准中引入了几个头文件来支持多线程编程:(因此咱们能够再也不使用 CreateThread 来建立线程,简简单单地使用 std::thread 便可。)

  • < thread > :包含std::thread类以及std::this_thread命名空间。管理线程的函数和类在 中声明.
  • < atomic > :包含std::atomic和std::atomic_flag类,以及一套C风格的原子类型和与C兼容的原子操做的函数。
  • < mutex > :包含了与互斥量相关的类以及其余类型和函数
  • < future > :包含两个Provider类(std::promise和std::package_task)和两个Future类(std::future和std::shared_future)以及相关的类型和函数。
  • < condition_variable > :包含与条件变量相关的类,包括std::condition_variable和std::condition_variable_any。

3.1 初试多线程

   一、主进程等待子线程

  

#include <iostream>
#include <thread>
#include <Windows.h>
 
using namespace std;
 
void thread01()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 01 is working !" << endl;
		Sleep(100);
	}
}
void thread02()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 02 is working !" << endl;
		Sleep(200);
	}
}
 
int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.join();
	task02.join();
 
	for (int i = 0; i < 5; i++)
	{
		cout << "Main thread is working !" << endl;
		Sleep(200);
	}
	system("pause");
}

  2.主进程和子进程互不干扰

  

#include <iostream>
#include <thread>
#include <Windows.h>
 
using namespace std;
 
void thread01()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 01 is working !" << endl;
		Sleep(100);
	}
}
void thread02()
{
	for (int i = 0; i < 5; i++)
	{
		cout << "Thread 02 is working !" << endl;
		Sleep(200);
	}
}
 
int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.detach();
	task02.detach();
 
	for (int i = 0; i < 5; i++)
	{
		cout << "Main thread is working !" << endl;
		Sleep(200);
	}
	system("pause");
}

  3.带参数的子线程

#include <iostream>
#include <thread>
#include <Windows.h>
 
using namespace std;
 
//定义带参数子线程
void thread01(int num)
{
	for (int i = 0; i < num; i++)
	{
		cout << "Thread 01 is working !" << endl;
		Sleep(100);
	}
}
void thread02(int num)
{
	for (int i = 0; i < num; i++)
	{
		cout << "Thread 02 is working !" << endl;
		Sleep(200);
	}
}
 
int main()
{
	thread task01(thread01, 5);  //带参数子线程
	thread task02(thread02, 5);
	task01.detach();
	task02.detach();
 
	for (int i = 0; i < 5; i++)
	{
		cout << "Main thread is working !" << endl;
		Sleep(200);
	}
	system("pause");
}

  4.多线程竞争的状况

有两个问题,一是有不少变量被重复输出了,而有的变量没有被输出;二是正常状况下每一个线程输出的数据后应该紧跟一个换行符,但这里大部分倒是另外一个线程的输出。

这是因为第一个线程对变量操做的过程当中,第二个线程也对同一个变量进行各操做,致使第一个线程处理完后的输出有多是线程二操做的结果。针对这种数据竞争的状况,可使用线程互斥对象mutex保持数据同步。

mutex类的使用须要包含头文件mutex:

#include <iostream>
#include <thread>
#include <Windows.h>
#include <mutex>
 
using namespace std;
 
mutex mu;  //线程互斥对象
 
int totalNum = 100;
 
void thread01()
{
	while (totalNum > 0)
	{
		mu.lock(); //同步数据锁
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
		mu.unlock();  //解除锁定
	}
}
void thread02()
{
	while (totalNum > 0)
	{
		mu.lock();
		cout << totalNum << endl;
		totalNum--;
		Sleep(100);
		mu.unlock();
	}
}
 
int main()
{
	thread task01(thread01);
	thread task02(thread02);
	task01.detach();
	task02.detach();
	system("pause");
}

  

 3.2 在类中使用子线程的一个问题

  当咱们再类中使用子线程咱们会发现,咱们不能把初始函数设置为类的成员函数,必需要把成员函数设置成static类型的才能够,可是这有设计到一个问题,就是static的类成员函数不能调用非static的变量成员,下面是一个一箭双鵰的方法:

thread sendtask(bind(&client::sendata, this));//其中client是类的名字

  这样就能够解决咱们的问题。

相关文章
相关标签/搜索