• C++(四)--线程与进程


    c98标准中不支持线程创建,c11标准中才有线程创建支持。

    目前windows和linux系统都自带创建进程和线程函数,进程process,线程thread。

    1、进程与线程

    不管是后台应用还是前台应用,我们一般喜欢说后台程序或者前台程序,即我们可以先理解为进程就是执行中的程序。任何程序一启动就是个(父)进程,同时自身也是一个主线程。从图1中可以看到,启动一个程序会分配很多资源。线程是在进程内创建的,一个进程中的所有线程在同一个地址空间中执行,共享程序的代码和数据结构,如图1中所示。即进程就是程序加上所有在该程序中执行的线程。

    比如我们一楼有个图书柜,那这个图书柜的存在就相当于数据已经加载到内存中了,你在那边读计算机系统书,然后我过来拿起一本数据结构书读。对于这个整体,你和我是两个线程,图书柜+你+我是这个进程。如果你我读同一本书就比较麻烦了,就是多线程编程的问题了,两个线程读同一内存数据想想有啥办法不?

                                                                                 

                                                                                                                                             图1 程序内存痕迹

    2、进程与线程的区别

           现在操作系统都是能运行很多程序,一个程序起码有一个进程,一般开机后就会有几十个进程。即使回到20年前,单核的赛扬器,那时候的操作系统启动也是有几十个进程在运行,但是我们还是可以很多事情一起干,比如:一边开比特精灵下载,一边听音乐,还一边玩qq游戏。就是因为这些进程不是一直占有CPU,每个进程都会有个时间片。时间片一到就切换到其他进程在CPU上,时间短到我们觉察不出来。

    操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。线程,有时被称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。即进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位;线程是进程的一部分。

    打个比方:进程是火车,那线程就是其中的车厢。那这个火车有10节车厢,就相当于这个进程有10个线程。如果在同一时间从上海到太原的列车有5趟,就是5俩火车同时开,那就是有5个进程。相比而言,加一趟列车难,加一节车厢容易。如果一节车厢坏了,处理不好会影响这辆火车;但是一趟并行的火车坏了,并不会影响其他趟火车。车厢之间可以相互走动,但各趟火车之间没法走动。

    通过火车例子可以看出,同一个进程内的多个线程之间数据可以分享,各个进程之间的数据是隔离的,跨进程数据交互需要通过进程间通讯机制等外力才行;创建进程或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销,但是进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响;线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个进程死掉就等于所有的线程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

    所以,在进程与线程之间,没有说哪个是最好的,只有跟据不同的应用场景择优使用,一般都是多进程多线程一起使用。

    3、系统接口

    创建子进程进程

    windows:CreateProcess(),参数比较多,可以指定某个可执行程序,不一定是自身程序。

    BOOL CreateProcess
     
    (
        LPCTSTR lpApplicationName,        
        LPTSTR lpCommandLine,        
        LPSECURITY_ATTRIBUTES lpProcessAttributes。
        LPSECURITY_ATTRIBUTES lpThreadAttributes,        
        BOOL bInheritHandles,        
        DWORD dwCreationFlags,
        LPVOID lpEnvironment,        
        LPCTSTR lpCurrentDirectory,        
        LPSTARTUPINFO lpStartupInfo,        
        LPPROCESS_INFORMATION lpProcessInformation 
    );

    linux:

    pid_t fork(void);

    返回值大于0表示现在运行的是父进程,等于0表示运行的是子进程,-1表示创建进程失败。

    启动进程

    #include <unistd.h>
    
    int execl(const char *path, const char *arg, ...);

    我们后台应用就有这个。假设有个businesscenter和business两个可执行程序,businesscenter相当于business的控制中心/统一调度程序,那么可以让businesscenter程序拉起business程序。具体做法是:在ini配置文件写上business所在的路径、可执行程序名,以及启动几个。如果写了5个,那么在businesscenter程序中就要fork5个子进程。在子进程中调用exec函数,如果失败了会返回,成功了就不会返回了,直到新程序运行结束。fork会将调用进程的所有内容原封不动的拷贝到新产生的子进程中,很耗费资源。所以,操作系统都有做了对应优化。使得fork结束后如果遇到exec函数并不立刻复制父进程的内容,而是到了真正实用的时候才复制。

    线程

    windows:

    HANDLE CreateThread(
        LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD:线程安全相关的属性,常置为NULL
        SIZE_T dwStackSize,//initialstacksize:新线程的初始化栈的大小,可设置为0
        LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction:被线程执行的回调函数,也称为线程函数
        LPVOID lpParameter,//threadargument:传入线程函数的参数,不需传递参数时为NULL
        DWORD dwCreationFlags,//creationoption:控制线程创建的标志
        LPDWORD lpThreadId//threadidentifier:传出参数,用于获得线程ID,如果为NULL则不返回线程ID
        )

    linux:

    #include<pthread.h>
    int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
    void *(*start_rtn)(void*),void *arg);

    C11:

    // thread example
    #include <iostream>       // std::cout
    #include <thread>         // std::thread
     
    void foo() 
    {
      // do stuff...
    }
    
    void bar(int x)
    {
      // do stuff...
    }
    
    int main() 
    {
      std::thread first (foo);     // spawn new thread that calls foo()
      std::thread second (bar,0);  // spawn new thread that calls bar(0)
    
      std::cout << "main, foo and bar now execute concurrently...
    ";
    
      // synchronize threads:
      first.join();                // pauses until first finishes
      second.join();               // pauses until second finishes
    
      std::cout << "foo and bar completed.
    ";
    
      return 0;
    }

    4、代码编写难易度

    我们写的代码都是顺序执行的,有些场景需要并行,那多线程的价值就存在了。像我们有个程序,一个进程里有两个线程,主线程主要用来是接收其他系统发来的tcp数据;子线程是从管道(文件)中读取数据,然后做业务逻辑处理。

    多线程因为可以共享数据,所以多个线程对共享数据的那块内存进行写操作时候,需要加锁,对锁处理不好会引起死锁,忙等待,导致程序的僵死,这块比较难,所以觉得要比写多进程复杂。

    参考资料:

    1、https://www.zhihu.com/question/25532384

    2、https://blog.csdn.net/ThinkWon/article/details/102021274

    3、https://blog.csdn.net/daaikuaichuan/article/details/82951084

    4、https://www.cnblogs.com/wanghetao/archive/2011/11/06/2237937.html

  • 相关阅读:
    cnn softmax regression bp求导
    使用kd-tree加速k-means
    KDTree详解及java实现
    加入商品分类信息,考虑用户所处阶段的 图模型 推荐算法 Rws(random walk with stage)
    用户标签
    LDA(latent dirichlet allocation)
    对物品进行反馈 代码
    1.虚拟机中安装ubuntu
    4.动态HTML处理和机器图像识别
    3.非结构化数据与结构化数据提取
  • 原文地址:https://www.cnblogs.com/ikel/p/13724430.html
Copyright © 2020-2023  润新知