C++ 11 多线程与线程管理



  • C++ 11 多线程与线程管理


             说到多线程编程,那么就不得不提并行并发

             并行是指两个或多个独立的操作同时进行。注意这里是同时进行,区别于并发,在一个时间段内执行多个操作。在单核时代,多个线程是并发的,在一个时间段内轮流执行;在多核时代,多个线程可以实现真正的并行,在多核上真正独立的并行执行。例如现在常见的4核4线程可以并行4个线程;4核8线程则使用了超线程技术,把一个物理核模拟为2个逻辑核心,可以并行8个线程。

             通常,要实现并发有两种方法:多进程和多线程

    • 多进程并发

             使用多进程并发是将一个应用程序划分为多个独立的进程(每个进程只有一个线程),这些独立的进程间可以互相通信,共同完成任务。由于操作系统对进程提供了大量的保护机制,以避免一个进程修改了另一个进程的数据,使用多进程比多线程更容易写出安全的代码。但这也造就了多进程并发的两个缺点:

             在进程件的通信,无论是使用信号、套接字,还是文件、管道等方式,其使用要么比较复杂,要么就是速度较慢或者两者兼而有之。
    运行多个线程的开销很大,操作系统要分配很多的资源来对这些进程进行管理。
    由于多个进程并发完成同一个任务时,不可避免的是:操作同一个数据和进程间的相互通信,上述的两个缺点也就决定了多进程的并发不是一个好的选择。

    • 多线程并发

             多线程并发指的是在同一个进程中执行多个线程。有操作系统相关知识的应该知道,线程是轻量级的进程,每个线程可以独立的运行不同的指令序列,但是线程不独立的拥有资源,依赖于创建它的进程而存在。也就是说,同一进程中的多个线程共享相同的地址空间,可以访问进程中的大部分数据,指针和引用可以在线程间进行传递。

             这样,同一进程内的多个线程能够很方便的进行数据共享以及通信,也就比进程更适用于并发操作。由于缺少操作系统提供的保护机制,在多线程共享数据及通信时,就需要程序员做更多的工作以保证对共享数据段的操作是以预想的操作顺序进行的,并且要极力的避免死锁(deadlock)

    • C++ 11的多线程简单示例
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    void output(int i)
    {
        cout << i << endl;
    }
    
    int main()
    {
        
        for (uint8_t i = 0; i < 4; i++)
        {
            thread t(output, i);
            t.detach(); 
        }
            
        getchar();
        return 0;
    }
    

             而运行结果,真如你想象的相同吗?
             我在Linux下用gcc编译后跑了一下,结果层出不穷。

             不是说好的并行么,同时执行,怎么还有先后的顺序?这就涉及到多线程编程最核心的问题了资源竞争。CPU有4核,可以同时执行4个线程这是没有问题了,但是控制台却只有一个,同时只能有一个线程拥有这个唯一的控制台,将数字输出。

    • 等待线程执行结束

             当线程启动后,一定要在和线程相关联的thread销毁前,确定以何种方式等待线程执行结束。C++11有两种方式来等待线程结束

             detach方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。前面代码所使用的就是这种方式。

             join方式,等待启动的线程完成,才会继续往下执行。假如前面的代码使用这种方式,其输出就会0,1,2,3,因为每次都是前一个线程输出完成了才会进行下一个循环,启动下一个新线程。

             无论在何种情形,一定要在thread销毁前,调用t.join或者t.detach,来决定线程以何种方式运行。当使用join方式时,会阻塞当前代码,等待线程完成退出后,才会继续向下执行;而使用detach方式则不会对当前代码造成影响,当前代码继续向下执行,创建的新线程同时并发执行。

    • 向线程传递参数

             向线程调用的函数传递参数也是很简单的,只需要在构造thread的实例时,依次传入即可。例如:

    void func(int *a,int n) { }
    
    int buffer[10];
    thread t(func,buffer,10);
    t.join();
    
    

    最后,来看一个多线程的程序demo

    #include <iostream>
    #include <pthread.h>
     
    using namespace std;
     
    #define NUM_THREADS 5
     
    // 线程的运行函数
    void* say_hello(void* args)
    {
        cout << "Hello Dian Group!" << endl;
        return 0;
    }
     
    int main()
    {
        // 定义线程的 id 变量,多个变量使用数组
        pthread_t tids[NUM_THREADS];
        for(int i = 0; i < NUM_THREADS; ++i)
        {
            //参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
            int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
            if (ret != 0)
            {
               cout << "pthread_create error: error_code=" << ret << endl;
            }
        }
        //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
        pthread_exit(NULL);
    }
    

    使用 -lpthread 库编译程序:

    $ g++ Dian.cpp -lpthread -o Dian.o

    执行程序,将产生下列结果:

    $ ./Dian.o
    Hello Dian Group!
    Hello Dian Group!
    Hello Dian Group!
    Hello Dian Group!
    Hello Dian Group!
    

 

Copyright © 2018 bbs.dian.org.cn All rights reserved.

与 Dian 的连接断开,我们正在尝试重连,请耐心等待