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...\n";

  // synchronize threads:
  first.join();                // pauses until first finishes
  second.join();               // pauses until second finishes

  std::cout << "foo and bar completed.\n";

  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

posted @ 2020-09-24 15:47  超龄码农  阅读(792)  评论(0编辑  收藏  举报