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)。若一个线程的时光片了,另一个线程C++的年华片刚开始时,上下文的切换
(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) 获取任务函数返回值 fut1.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