C++11 并发编程教程 - Part 1 : thread 初探(bill译)

C++11 引入了一个新的线程库,包含了用于启动、管理线程的诸多工具,与此同时,该库还提供了包括互斥量、锁、原子量等在内的同步机制。在这个系列的教程中,我将尝试向你们展现这个新库提供的大部分特性。
ios

   为了可以编译本文的示例代码,你须要有一个支持 C++11 的编译器,笔者使用的是 GCC4.6.1(你须要添加 "-std=c++11" 或 "-std=c++0x" 编译选项以启动 GCC  C++11 的支持)[译注:bill 的编译环境为 GCC4.6.3 + codeblocks 10.05 + Ubuntu 12.04,所使用的编译选项为 "-std=gnu++0x"]。
c++


启动线程 并发

   启动一个新的线程很是简单,当你建立一个 std::thread 的实例时,它便会自行启动。建立线程实例时,必须提供该线程将要执行的函数,方法之一是传递一个函数指针,让咱们以经典的 "Hello world" 来阐释这一方法: 函数


1
2
3
4
5
6
7
8
9
10
#include <thread>
#include <iostream>
void  hello(){
    std::cout <<  "Hello from thread "  << std::endl;
}
int  main(){
    std::thread t1(hello);
    t1.join();
    return  0;
}


   全部的线程工具均置于头文件 <thread> 中。这个例子中值得注意的是对函数 join() 的调用。该调用将致使当前线程等待被 join 的线程结束(在本例中即线程 main 必须等待线程 t1 结束后方可继续执行)。若是你忽略掉对 join() 的调用,其结果是未定义的 —— 程序可能打印出 "Hello from thread" 以及一个换行,或者只打印出 "Hello from thread" 却没有换行,甚至什么都不作,那是由于线程main 可能在线程 t1 结束以前就返回了。 工具


区分线程 this

   每一个线程都有惟一的 ID 以便咱们加以区分。使用 std::thread 类的 get_id() 即可获取标识对应线程的惟一 ID。咱们可使用 std::this_thread 来获取当前线程的引用。下面的例子将建立一些线程并使它们打印本身的 ID spa


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <thread>
#include <iostream>
#include <vector>
void  hello(){
    std::cout <<  "Hello from thread "  << std::this_thread::get_id() << std::endl;
}
int  main(){
    std::vector<std::thread> threads;
    for(int  i = 0; i < 5; ++i){
        threads.push_back(std::thread(hello));
    }
    for(auto&  thread  : threads){
        thread.join();
    }
    return  0;
}

   依次启动线程并将他们存入 vector 是管理多个线程的经常使用伎俩,这样你即可以轻松地改变线程的数量。回到正题,就算是上面这样短小简单的例子,也不能判定其输出结果。理论状况是: 线程


1
2
3
4
5
Hello from thread 140276650997504
Hello from thread 140276667782912
Hello from thread 140276659390208
Hello from thread 140276642604800
Hello from thread 140276676175616


   但实际上(至少在我这里)上述状况并不常见,你极可能获得的是以下结果: 指针


1
2
3
4
Hello from thread Hello from thread Hello from thread 139810974787328Hello from thread 139810983180032Hello from thread
139810966394624
139810991572736
139810958001920

   或者更多其余的结果。这是由于线程之间存在 interleaving 。你没办法控制线程的执行顺序,某个线程可能随时被抢占,又由于输出到 ostream 分几个步骤(首先输出一个 string,而后是 ID,最后输出换行),所以一个线程可能执行了第一个步骤后就被其余线程抢占了,直到其余全部线程打印完以后才能进行后面的步骤。 c++11


使用 Lambda 表达式启动线程

   当线程所要执行的代码很是短小时,你没有必要专门为之建立一个函数,取而代之的是使用 Lambda表达式。咱们能够很轻易地将上述例子改写为使用 Lambda 表达式的形式:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <thread>
#include <iostream>
#include <vector>
int  main(){
    std::vector<std::thread> threads;
    for(int  i = 0; i < 5; ++i){
        threads.push_back(std::thread([](){
            std::cout <<  "Hello from thread "  << std::this_thread::get_id() << std::endl;
        }));
    }
    for(auto&  thread  : threads){
        thread.join();
    }
    return  0;
}

   如上,咱们使用了 Lambda 表达式替换掉原来的函数指针。毋庸置疑,这段代码和以前使用函数指针的代码实现了彻底相同的功能。


下篇

   在本系列的下一篇文章中,咱们将看到如何使用锁机制保护咱们的并发代码。

相关文章
相关标签/搜索