c114|C++11 并发编程示例教程

更新时间:2019-12-11    来源:面向对象编程    手机版     字体:

【www.bbyears.com--面向对象编程】

C++11标准支持了并发,其中包含了线程管理,共享资源保护,线程间同步操作和底层原子操作等功能。我们先通过一个简单的示例看看C++11标准的多线程程序是什么样的。

#include <iostream>  
#include  // 引用用于管理线程的类的头文件  
  
using namespace std;  
  
// 线程的入口函数,程序将在新创建的线程中打印log  
void hello()  
{  
    cout << "Hello Concurrent World" << endl;  
}  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    // 构造线程对象t,设置函数hello为其入口函数  
    thread t(hello);  
    // 线程t被创建后,主线程继续执行,将子线程加入到主线程中,这样主线程就会等待子线程结束  
    // 否则,主线程可能先于子线程结束而退出  
    t.join();  
  
    return 0;  
}


程序执行结果:


01.jpeg

join

1. 等待线程完成

若不等待线程完成,我们就需要确保该线程访问的数据都是有效的,直到该线程完成为止。比如如下代码,线程函数持有局部变量的指针或引用,当函数退出时,线程尚未执行完成。


#include   
#include <iostream>  
  
  
// 线程持有局部变量的指针  
struct func  
{  
    int *i;  
    func(int *i_) : i(i_){  
      
    }  
  
    void operator()()  
    {  
        for (unsigned j = 0; j < 100000; ++j)  
        {  
            *i = j; // 访问非法地址  
        }  
    }  
};  
  
// 不等待线程执行完成就退出  
void oops()  
{  
    int some_local_state = 0;  
    func my_func(&some_local_state);  
    std::thread my_thread(my_func);  
    my_thread.detach();  
}  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    oops();  
  
    return 0;  
}



要避免这种情况,需要使用std::thread实例的join()来替换my_thread.detach()的调用,这样就可以保证在函数退出前,线程已经结束。对一个给定的线程,只能调用一次join(),一旦调用了join(),此std::thread对象不再是可连接的,如果调用其的joinable()将返回false。

2. 在异常环境下的等待

我们需要在线程对象被销毁前调用join或detach方法,如果要detach,通常在线程启动后就立即调用detach方法。如果打算等待该线程,就需要仔细的选择在哪个位置调用join。如果在线程开始之后,调用join之前发生了异常,则可能跳过对join的调用。

为了避免应用程序在引发异常的时候被终止,你需要异常时也调用join。

void do_something_in_current_thread()  
{  
    throw("error");  
}  
  
// 不等待线程执行完成就退出  
void oops()  
{  
    int some_local_state = 0;  
    func my_func(&some_local_state);  
    std::thread my_thread(my_func);  
  
    try  
    {  
        do_something_in_current_thread();  
    }  
    catch (const char *err_msg)  
    {  
        my_thread.join();  
        throw;  
    }  
      
    my_thread.join();  
}


try/catch块可以确保无论函数时正常退出还是异常退出,都调用了线程的join方法,但是try/catch块看起来很??拢?踩菀椎贾伦饔糜诨炻遥??虻サ陌旆ㄊ鞘褂?AII-Resource Acquisition Is Initialization并提供一个类,在析构函数中调用join():

class thread_guard  
{  
    std::thread& t;  
public:  
    explicit thread_guard(std::thread& t_) :  
        t(t_)  
    {  
    }  
  
    // 析构函数中检查线程是否还未被join,若没有,则调用  
    ~thread_guard()  
    {  
        if (t.joinable())  
        {  
            t.join();  
        }  
    }  
  
    // 将拷贝后赋值运算符标记为=delete以避免编译器自动生成,复制或赋值这样一个对象可能很危险,因为它可能比它要结合的线程的作用域存在得更久。  
    thread_guard(thread_guard const&) = delete;  
    thread_guard& operator=(thread_guard const&) = delete;  
}


本文来源:http://www.bbyears.com/jsp/82791.html

热门标签

更多>>

本类排行