C++11新特性:std::thread多线程

       在C++11以前,C++的多线程编程在不同到平台使用不同的API,比如linux平台使用pthreadwindows平台使用winSDK中的Create,或者依赖其他第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口。

  我们通过如下几部分介绍C++11多线程方面的接口及使用方法。

1、使用例子

#include

#include

 

void threadfun1()

{

    std::cout << "I am threadfun1" << std::endl;

}

 

void threadfun2(int iParam, std::string sParam)

{

    std::cout << "I am threadfun2 - para:" << iParam << std::endl;

}

 

int main()

{

    std::thread t1(threadfun1);

    std::thread t2(threadfun2, 10, "hello");

    std::this_thread::sleep_for(std::chrono::milliseconds(10));

    t1.join();

    std::cout << "t1 join" << std::endl;

    t2.detach();

    std::cout << "t2 detach" << std::endl;

    system("pause");

}

运行结果:

 

有以上输出结果是由于通过延时控制的,以下通道一些实例来说明std::thread工作原理。

2、原理分析

1

#include

#include

 

void thread1() {

    for (int i = 0; i<10; ++i)

        std::cout << "I am thread1..." << std::endl;

}

 

void thread2() {

    for (int i = 0; i<10; ++i)

        std::cout << "I am thread2..." << std::endl;

}

 

int main(int argc, char* argv[]) {

    std::thread th1(thread1); // 实例化线程对象

    std::thread th2(thread2);

    std::cout << "out:main..." << std::endl;

    return 0;

}

 

 

运行结果出错,证明例1代码是有问题的。因为主函数(main)在创建完thread对象之后马上结束了主线程,而此时创建出来的线程还在继续运行并还处于joinable状态,即线程还在继续存在而线程对象已经被销毁,从而导致了异常出现。

 

可以通道join解决以上问题:

例2:

#include

#include

 

void thread1() {

    for (int i = 0; i<10; ++i)

        std::cout << "thread1..." << std::endl;

}

 

void thread2() {

    for (int i = 0; i<10; ++i)

        std::cout << "thread2..." << std::endl;

}

 

int main(int argc, char* argv[]) {

    std::thread th1(thread1);   // 实例化线程对象

    std::thread th2(thread2);

    std::cout << "th1->1 status : " << th1.joinable() << std::endl;

    th1.join();

    std::cout << "th1->2 status : " << th1.joinable() << std::endl;

    th2.join();

    std::cout << "out : main..." << std::endl;

    system("pause");

    return 0;

}

输出结果:

 

结果显示join只能运行一次,join之后对应的线程joinable状态变成false(即线程已经被join,不可再join)。同时该代码可以正常运行,但是存在一个问题,就是主线程一定要等待线程运行完成。如果让main不需要等待thread完成,可以使用detach来实现

 

例3:

#include

#include

 

void thread1() {

    for (int i = 0; i<10; ++i)

        std::cout << "thread1..." << std::endl;

}

 

void thread2() {

    for (int i = 0; i<10; ++i)

        std::cout << "thread2..." << std::endl;

}

 

int main(int argc, char* argv[]) {

    std::thread th1(thread1);   // 实例化线程对象

    std::thread th2(thread2);

    th1.detach();

    th2.detach();

    std::cout << "out : main..." << std::endl;

    system("pause");

    return 0;

}

运行结果:

 

可以看到main在创建完thread对象之后马上结束,但创建的线程还可以继续执行。Detach的意思是把当前创建线程对象所代表的实例与宿线程对象分离,使得线程可以独立运行,在线程完成后可以自动释放线程资源。但是这种方式也存在问题,就是线程失去了控制,在主线程结束时该线程的工作可能还没完成,导致线程所做的工作可能出现问题。一下重要的线程建议不要采用这种方式。

3、线程互斥

3.1 std::mutex

#include

#include

#include

std::mutex m;

int cnt = 15;

 

void thread1() {

    m.lock();

    for(; cnt>10; cnt--)

        std::cout << "thread1: cnt=" << cnt << std::endl;

    //return;     // 手动制作线程异常

    m.unlock();

}

 

void thread2() {

    m.lock();

    cnt -= 10;

    std::cout << "thread2: cnt=" << cnt << std::endl;

    m.unlock();

}

 

int main(int argc, char* argv[]) {

    std::thread th1(thread1);  

    std::thread th2(thread2);

    th1.join();

    th2.join();

    std::cout << "out : main..." << std::endl;

    system("pause");

    return 0;

}

 

输出结果:

 

Mutex不是线程安全锁,如上代码在thread1中打开手动异常或者其他问题导致在解锁钱出现线程异常退出,会导致锁一直没有被释放,其他线程也就无法获取锁。

3.2 std::lock_guard

       该锁是一个线程安全锁,它是基于作用域的锁,能够自解锁,当创建所对象后,可以通道lock()方法获取锁,当生命周期解锁后自动调用析构函数释放锁(unlock),因此不会出现mutex的问题。

#include

#include

#include

 

std::mutex m;

int cnt = 15;

 

void thread1() {

    std::lock_guard lock(m);

    for (; cnt>10; cnt--)

        std::cout << "thread1: cnt=" << cnt << std::endl;

    return;     // 手动制作线程异常

}

 

void thread2() {

    std::lock_guard lock(m);

    cnt -= 10;

    std::cout << "thread2: cnt=" << cnt << std::endl;

}

 

int main(int argc, char* argv[]) {

    std::thread th1(thread1);

    std::thread th2(thread2);

    th1.join();

    th2.join();

    std::cout << "out : main..." << std::endl;

    system("pause");

    return 0;

}

运行结果:

 

可以看到就算在thread1退出前手动制作异常,两个线程也可以正常运行。

4、其他

1)swap()函数可以交换两个线程的句柄,如thr1.swap(th2);再用get_id发现两个线程已经交换了id

2)std::thread的拷贝构造函数是被禁用的,thread th2(th1)会不成功。

 

你可能感兴趣的:(C/C++)