C++11 之 并发编程 (一)

未来芯片制造,如果突破不了 5nm 极限,那么在一段时间内 CPU 性能的提升,会依赖于三维集成技术,将更多的 CPU 核集成在一起,使得多核系统越来越普遍。

以前所谓的 C++ 多线程,一是受限于平台,多借助于封装好的 APIs 来完成,例如:POSIX threads,Windows threads 等;二是受限于单核系统,本质上都是“伪多线程”,通过线程的调度,使得单核系统可进行任务的切换,造成多线程的假象。

新的 C++11 标准,在语言层面上实现了多线程,其库中提供了相关组件,使得跨平台编写多线程 cpp 程序成为可能,最终能够在多核系统中实现真正的并行计算

1  并发 (concurrency)

1.1 并发与并行

计算机中的并发,指的是在单一系统中,同时执行多个独立的活动。对于多核系统,它们在同一时刻进行的活动,则称为并行

通俗的理解:当有两个人都在边吃饭边看电视时,对于一个人而言,吃饭和看电视就是并发,对于两个人而言,他们在同一时刻进行的活动,可称为并行。

1) 单核的任务切换 (task switching)

2) 双核的并行执行 (parallel execution)

1.2 线程与进程

如果将进程比作一套房子,那么住在房子里的人,其从事的各种活动 (比如,厨房做饭,客厅吃饭,卧室看电视),就是一个个的线程。现在又搬来一个人,则当两个人都在房子里,做着各自的活动时,程序便由以前的单线程变为了多线程

有的房间 (比如客厅) 两个人都可以同时进出,这代表着进程中某些内存空间是共享的,每个线程都可以使用这些共享内存。有的房间 (比如厕所) 一次只能容纳一个人,先进去的把门锁上,后到的人看到锁,就在外面等待,直到先进去的人把锁打开,这就是“互斥核” (mutex)

在应用程序中,具体利用到并发编程的,一是多进程并发,二是多线程并发,如下图:

       

(1) 多进程并发                     (2) 多线程并发

2  程序示例

  实现多线程需要 1) 头文件 <thread>   2) 单独的线程函数 threadFunc()   3)线程对象 thread t(threadFunc)   4)等待线程 join()

#include <thread>
#include <iostream>

void threadFunc()
{
    std::cout << "This is a thread!" << std::endl;
}

int main()
{
    std::thread t(threadFunc);
    t.join();

    std::cout << "This is main program!" << std::endl;

    return 0;
}

输出结果为:

This is a thread!
This is main program!

当使用 t.detach() 来代替 t.join() 时,主线程 main 不会等待新线程 t(threadFunc),只会独自运行到程序结束。

3  任务代替线程

3.1  两个问题

1) 线程耗尽 (exhaustion)

 软件线程是一种有限的资源,当创建的线程数量多于系统所能够提供的,一个异常 std::system_error 就会抛出,程序便会终止。

2) 线程超额 (oversubscription)

 当等待运行的线程 (ready-to-run) 多于系统硬件线程 (hardware threads) 时,线程调度器会为每个软件线程在硬件线程上分配时间片 (time-slice)。若一个线程的时间片结束,另一个线程的时间片刚开始时,上下文的切换 (context switch) 就会被执行。对于多核系统,当线程从一个 CPU 被切换到另一个 CPU 中时,会造成很大的资源消耗。

3.2  基于任务 (task-based)

基于线程编程 (thread-based),必须手动管理上面的两个问题,增加了编程的难度。

基于任务编程 (task-based),通过 std::async,可将问题交由 C++标准库处理,简化了程序。

1)  头文件 <future>

2) 单独的任务函数 taskFunc1 和 taskFunc2

3) 异步任务对象 auto fut1 = std::async(taskFunc1)

4) 获取任务函数返回值 fut.get()

#include <iostream>
#include <thread>
#include <future>

std::string taskFunc1()
{
    std::string str = "This is task1";
    return str;
}

std::string taskFunc2()
{
    std::string str = " and task2";
    return str;
}

int main()
{
    auto fut1 = std::async(taskFunc1);
    auto fut2 = std::async(taskFunc2);

    std::cout << fut1.get() + fut2.get() << std::endl << "This is main program" << std::endl;

    return 0;
}

输出结果为:

This is task1 and task2
This is main program

小结:

1) thread-based programming needs manual management of thread exhaustion, oversubscription, load balancing, and adaptation to new platforms.

2) task-based programming handles most of these issues via std::async with the default launch policy

参考资料:

<C++ Concurrency in Action> chapter 1

<Effecctive Modern C++>  chapter 7

时间: 2024-10-13 22:53:03

C++11 之 并发编程 (一)的相关文章

Java多线程视频教程并发编程面试知识

课程目录:  1-1.并发编程入门到实战课程简介1-2.什么是并发编程1-3.并发编程的挑战之频繁的上下文切换1-4.并发编程的挑战之死锁1-5.并发编程的挑战之线程安全1-6.并发编程的挑战之资源限制2-1.进程与线程的区别2-2.线程的状态及其相互转换2-3.创建线程的方式(上)2-4.创建线程的方式(下)2-5.线程的挂起及其恢复2-6.线程的中断操作2-7.线程的优先级2-8.守护线程3-1.什么是线程安全性3-2.从字节码角度剖析线程不安全操作3-3.原子性操作3-4.深入理解sync

C++11 并发编程基础(一):并发、并行与C++多线程

正文 C++11标准在标准库中为多线程提供了组件,这意味着使用C++编写与平台无关的多线程程序成为可能,而C++程序的可移植性也得到了有力的保证.另外,并发编程可提高应用的性能,这对对性能锱铢必较的C++程序员来说是值得关注的. 回到顶部 1. 何为并发 并发指的是两个或多个独立的活动在同一时段内发生.生活中并发的例子并不少,例如在跑步的时候你可能同时在听音乐:在看电脑显示器的同时你的手指在敲击键盘.这时我们称我们大脑并发地处理这些事件,只不过我们大脑的处理是有次重点的:有时候你会更关注你呼吸的

并发编程: c++11 thread(Func, Args...)利用类成员函数创建线程

c++11是VS2012后支持的新标准,为并发编程提供了方便的std::thread. 使用示例: #include <thread> void thread_func(int arg1, int arg2, float* arg3){ arg3 = (arg1*1.0)/(arg1 + arg2); cout << "arg1 / (arg1 + arg2) = " << arg3 << endl; return; } void mai

java并发编程之美-阅读记录11

java并发编程实践 11.1ArrayBlockingQueue的使用 有关logback异步日志打印中的ArrayBlockingQueue的使用 1.异步日志打印模型概述 在高并发.高流量并且响应时间要求比较小的系统中同步打印日志在性能上已经满足不了了,这是以因为打印本身是需要写磁盘的,写磁盘操作会暂时阻塞调用打印日志的业务系统,这会造成调用线程的响应时间增加.    ----- >>> 异步日志打印,是将打印日志任务放入一个队列后就返回,然后使用一个线程专门从队列中获取日志任务,

使用 C++11 并发编程入门

一.认识并发和并行 先将两个概念, 并发与并行 并发:同一时间段内可以交替处理多个操作: 图中整个安检系统是一个并发设计的结构.两个安检队列队首的人竞争这一个安检窗口,两个队列可能约定交替着进行安检,也可能是大家同时竞争安检窗口(通信).后一种方式可能引起冲突:因为无法同时进行两个安检操作.在逻辑上看来,这个安检窗口是同时处理这两个队列 并行:同一时刻内同时处理多个操作: 图中整个安检系统是一个并行的系统.在这里,每个队列都有自己的安检窗口,两个队列中间没有竞争关系,队列中的某个排队者只需等待队

【Java并发编程】11.P6的offer擦肩而过就因为我不懂synchronized原理

使用 synchronized关键字是并发编程中线程同步的常用手段之一,synchronized是悲观锁,其作用有三个: 互斥性:确保线程互斥的访问同步代,锁自动释放,多个线程操作同个代码块或函数必须排队获得锁,可见性:保证共享变量的修改能够及时可见,获得锁的线程操作完毕后会将所数据刷新到共享内存区有序性:有效解决重排序问题,其用法也有三个:修饰实例方法修饰静态方法修饰代码块1. 修饰实例方法 synchronized关键词作用在方法的前面,用来锁定方法,其实默认锁定的是this对象. publ

java并发编程11.原子变量与非阻塞同步机制

在非阻塞算法中不存在死锁和其他活跃性问题. 在基于锁的算法中,如果一个线程在休眠或自旋的同时持有一个锁,那么其他线程都无法执行下去,而非阻塞算法不会受到单个线程失败的影响. 锁的劣势 许多JVM都对非竞争锁获取和释放操作进行了极大的优化,但如果有多个线程同时请求锁,那么JVM就需要借助操作系统地功能.如果出现了这种情况,那么一些线程将被挂起并且在稍后恢复运行.当线程恢复执行时,必须等待其他线程执行完它们的时间片以后,才能被调度执行.在挂起和恢复线程等过程中存在着很大的开销,并且通常存在着较大时间

并发编程 11—— 任务取消与关闭 之 shutdownNow 的局限性

Java并发编程实践 目录 并发编程 01—— ConcurrentHashMap 并发编程 02—— 阻塞队列和生产者-消费者模式 并发编程 03—— 闭锁CountDownLatch 与 栅栏CyclicBarrier 并发编程 04—— Callable和Future 并发编程 05—— CompletionService : Executor 和 BlockingQueue 并发编程 06—— 任务取消 并发编程 07—— 任务取消 之 中断 并发编程 08—— 任务取消 之 停止基于线

VS C++ 并发编程

1.VS2012及以上版本,支持C++11 thread类的并发编程. 相关材料可以参考博客:http://www.cnblogs.com/rangozhang/p/4468754.html 2.但对其之前的版本,可采用以下方式,实现类成员函数创建子线程实现并发. 首先需实现线程类的run函数,故定义了线程类的头文件和其对应的函数实现,具体如图1,2所示: 图1 线程类的头文件 图2 线程类的实现文件 注意到继承的DerivedThread类,只需将并发执行的函数写在其对应的run()函数内即可