关于多进程与多线程

多进程
                1、进程的创建fork()
                    #include <unistd.h>
                    pid_t fork(void);
                    pid_t vfork(void);
                        返回值:    > 0       父进程 parent process
                                ==0       子进程 child process
                                < 0(-1)   失败
                        fork的特点:
                            第一个特点:    一次调用,两次返回,返回大于0(值就是子进程的id号)表示父进程,等于0表示子进程
                                        子进程会复制父进程的所有资源(代码段,数据段)
                            第二个特点:父进程总共分成三个部分
                                        第一个部分在fork之前
                                        第二个部分fork成功之后,在id>0情况下的那部分代码
                                        第三个部分fork后面的
                        总结:
                            第一点:fork()的套路
                                    if(>0){ 父进程代码 }else if(==0){ 程序员创建子进程需要并发执行的任务代码 }else{ 错误 }                     第二点:vfork创建的子进程共享父进程的资源
                            第二点:vfork创建的子进程一定优先于父进程运行
                2、进程的退出跟回收exit()/wait()
                    #include <stdlib.h>
                    void exit(int status); //退出进程的同时刷新缓冲区
                    void _exit(int status);//退出进程的时候不刷新缓冲区
                        参数:status -->进程退出时候的值
                        对比return:    区别一:return是C语言中关键字,exit()是函数
                                    区别二:return是返回函数调用的结果,exit()是结束整个进程
                    #include <sys/wait.h>
                    pid_t wait(int *stat_loc);--->收子进程结束返回值(收尸)
                        返回值:成功 返回值回收到的那个子进程的ID,失败 -1
                        参数:stat_loc -->用来存放进程退出时候的状态信息 
                                        不是存放退出值,退出值仅仅只是状态信息中的一部分
                        特点:会让父进程一直阻塞,直到成功回收到子进程为止          
                    pid_t waitpid(pid_t pid, int *stat_loc, int options);
                        返回值:成功 返回值回收到的那个子进程的ID,失败 -1
                        参数:    pid -->    小于-1   waitpid(-1200,&status,options);回收进程组id是1200的其中一个
                                        等于-1   waitpid(-1,&status,options);回收任意一个子进程(不要求同组)
                                        等于0    waitpid(0,&status,options); 回收本进程组中任意一个子进程
                                        大于0    waitpid(1200,&status,options); 指定回收id是1200的子进程
                                stat_loc -->跟wait一样
                                options -->WN0HANG  非阻塞等待,等得到就等,等不到就直接退出
                                            0        阻塞等待
                3、获取当前进程的id以及父进程的id
                    #include <sys/types.h>
                    #include <unistd.h>
                    pid_t getpid(void);  //获取子进程ID
                    pid_t getppid(void); //获取父进程ID
                    gid_t getgid(void); //获取进程组ID
                4、使用函数执行shell命令
                    #include <stdlib.h>
                    #include <unistd.h>
                    int system(const char *command);
                        返回值:失败 -1 
                        参数:    command -->你要执行的shell命令或者你要执行程序完整的命令名
                    int execl(const char *path, const char *arg, ...);
                        参数:    path -->你要执行的程序/命令所在的路径
                                arg -->你要执行的命令/程序
                    int execlp(const char *file, const char *arg, ...);
                    int execle(const char *path, const char *arg,..., char * const envp[]);
                    int execv(const char *path, char *const argv[]);
                    int execvp(const char *file, char *const argv[]);
                    int execvpe(const char *file, char *const argv[],char *const envp[]);
                        参数:    file-->即将被加载执行的文件名
                                argv-->保存即将被执行的参数的数组
                                envp-->用户自定义的环境变量数组
                        总结:      l -->参数以列表的形式逐一列举 
                                e -->参数中可以设置环境变量
                                v -->参数用一个指针数组存放
                                p -->传递程序/命令的名字
        进程间通信
            无名管道和有名管道
                1、创建无名管道
                    #include <unistd.h>
                    int pipe(int fildes[2]);
                        返回值:成功 0 失败 -1
                        参数:    fildes[2] -->存放的是两个文件描述符
                                fildes[0]读端的文件描述符
                                fildes[1]写端的文件描述符
                        特点:
                            有固定的读端(fd[0])跟写端(fd[1])
                            当管道中没有数据可以读的时候,调用read会阻塞
                            只能用于具有血缘关系的进程之间(父子进程,兄弟进程之间)
                2、创建有名管道
                    #include <sys/types.h>
                    #include <sys/stat.h>
                    int mkfifo(const char *pathname, mode_t mode);
                        返回值:成功 0 失败 -1
                        参数:    pathname -->你要创建的有名管道的名字(带路径)
                                mode -->0777 
                        特点:
                            有名字,生成一个管道文件,用于通信
                            当管道中没有数据可以读的时候,调用read会阻塞
                            任意两个不同进程之间都能通信
                3、判断文件是否存在
                    #include <unistd.h>
                    int access(const char *path, int amode);
                        功能:判断一个文件是否存在,参数用来判断文件是否可读、是否可写、是否可执行
                        返回值:符合你要求的判断条件返回0  否则 -1
                        参数:    path -->文件的路径
                                amode -->R_OK, W_OK,  X_OK ,F_OK
            信号:作用是当一个进程发送信号给另外一个进程的时候,可以通过该信号去控制另外一个进程执行程序员想干的事情
                1、发送信号
                    #include <signal.h>
                    int kill(pid_t pid, int sig);
                        参数:    pid -->你要发送信号的那个进程的id
                                sig -->你要发送的信号
                    int sigqueue(pid_t pid, int sig, const union sigval value);
                        参数:union sigval     {
                                              int   sival_int;
                                              void *sival_ptr; //void *万能指针
                                            };//存放你想发送的额外数据
                        跟kill的区别:sigqueue买一送一(发送信号的同时可以额外发送其他数据给到进程)
                2、捕捉信号并改变信号的响应动作
                    #include <signal.h>
                    void (*signal(int sig, void (*func)(int)))(int);
                        返回值:最近一次调用时第二个参数的值(函数指针)
                        参数:sig -->你想要捕捉的信号
                        第二个参数有三种情况:
                            情况一:
                                void (*func)(int) -->函数指针,你想要改变的信号动作就靠该函数实现
                            情况二:
                                SIG_DFL -->按照信号默认的动作响应
                            情况三:
                                SIG_IGN -->忽略信号,收到信号之后不做任何响应,直接舍弃
                        注意:在所有的信号中有两个信号是既不能改变默认动作也不能忽略,SIGKILL和SIGSTOP
                    int sigaction(int sig, const struct sigaction *restrict act,struct sigaction *restrict oact);
                        参数:    struct sigaction
                                {
                                      void(*)(int)          sa_handler //跟signal中函数指针一模一样
                                      sigset_t              sa_mask     //信号阻塞掩码??
                                      int                   sa_flags     //设置0选择sa_handler
                                                                                    //设置SA_SIGINFO表示选择sa_sigaction
                                      void(*)(int,siginfo_t *,void *)    sa_sigaction//另外一个信号响应函数,接收额外数据
                                }
                                void(*)(int,siginfo_t *,void *)
                                siginfo_t
                                {
                                  si_int -->存放union sigval里面的sival_int
                                  si_ptr -->存放union sigval里面的sival_ptr
                                  si_pid -->存放发送信号的那个进程的id
                                }
                                oact-->原有信号的处理参数,一般为NULL
                3、其它简单函数
                    #include <signal.h>
                    int pause(void);//阻塞当前进程等待信号到来
                    int raise(int sig);//自己给自己发送信号
                    unsigned alarm(unsigned seconds);//定时器,alarm(5);过5秒之后自己给自己发送SIGALRM 
                4、信号的阻塞或屏蔽
                    #include <signal.h>
                    int sigprocmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);//设置信号阻塞的函数
                        返回值:成功 0 失败 -1
                        参数:how -->SIG_BLOCK    //设置阻塞 将set对应信号添加到原本的信号集合中
                                    SIG_SETMASK  //设置阻塞 用set替换原本的信号集合中的信号
                                    SIG_UNBLOCK  //解除阻塞
                             sigset_t --> 系统定义的一种变量类型,专门用来存放你想要阻塞的信号
                                             称之为信号阻塞掩码集
                    操作信号阻塞掩码集合的函数
                    int sigemptyset(sigset_t *set); //清空掩码集
                    int sigfillset(sigset_t *set); //将所有的linux信号添加到集合中
                    int sigaddset(sigset_t *set, int signum);//将具体的某个信号添加到集合
                    int sigdelset(sigset_t *set, int signum);//将具体的某个信号从集合删除
                    int sigismember(const sigset_t *set, int signum);//判断某个信号在不在集合  返回1是成员   返回0不是成员
                    注意:信号设置阻塞仅仅只是将信号暂时挂起,信号依然存在(等到解除阻塞又能重新响应)
        system-V IPC通信 :指的就是共享内存,消息队列,信号量
            linux命令:    ipcs -s 查看当前系统所有的信号量
                        ipcs -m 查看当前系统所有的共享内存
                        ipcs -q 查看当前系统所有的消息队列
                        ipcs -a 查看当前系统所有的IPC对象
                        ipcrm -s 信号量id  删除信号量
                        ipcrm -m 共享内存id  删除共享内存
                        ipcrm -q 消息队列id  删除消息队列
            信号量:用来协调多个进程对应共享资源的访问
                特点:当信号量的值为0,你还想p操作,会阻塞当前进程
                     信号量的值是不可能为负数的
                     v操作永远不会阻塞
                1、创建信号量
                    #include <sys/sem.h>
                    #include <sys/ipc.h>
                    int semget(key_t key, int nsems, int semflg);
                        返回值:成功 信号量的id  失败 -1
                        参数:key -->键值,确保唯一性
                            产生键值两种方法
                            第一种:自己随便写一个(正整数)
                            第二种:使用系统提供的ftok()生成一个键值
                                #include <sys/ipc.h>
                                key_t ftok(const char *pathname, int proj_id);
                                    返回值:成功 返回键值  失败 -1
                                    参数:pathname -->合法的linux路径
                                          proj_id -->随便写个整数
                                    ftok(".",200);  ftok(".",199);
                            nsems -->你打算创建多少个信号量
                            semflg -->IPC_CREAT信号量不存在则创建
                                      IPC_EXCL信号量已存在则报错
                                      0777    信号量的访问权限
                2、获取或设置信号量的相关属性
                    #include <sys/sem.h>
                    #include <sys/ipc.h>
                    int semctl(int semid, int semnum, int cmd, ...);
                        参数:    semid -->信号量的ID,semget的返回值
                                semnum -->信号量的序号,从0开始
                                cmd -->GETVAL //获取信号量值
                                        int value=semctl(id,0,GETVAL);返回值给value    
                                       SETVAL //设置信号量值
                                        semctl(id,0,SETVAL,10);//将第一个信号量值设置为10
                                       IPC_RMID //删除信号量
                3、信号量的PV操作
                    #include <sys/sem.h>
                    #include <sys/ipc.h>
                    int semop(int semid, struct sembuf *sops, size_t nsops);
                        返回值:
                        参数:struct sembuf
                             {
                                  short        sem_num      信号量的序号
                                  short        sem_op       决定你究竟是想P操作还是V操作,负数P操作,正数V操作
                                  short        sem_flg      SEM_UNDO(操作完信号量之后,恢复成原本值)
                             }
                             nsops -->信号量struct sembuf个数
            共享内存:效率最高的IPC
                1、申请共享内存
                    #include <sys/shm.h>
                    #include <sys/ipc.h>
                    int shmget(key_t key, size_t size, int shmflg);
                        返回值:成功 共享内存的ID  失败 -1
                        参数:size -->你打算申请多少的内存,字节,一般设置成512的整数倍
                             shmflg->IPC_CREAT共享内存不存在则创建
                                     IPC_EXCL共享内存已存在则报错
                                     0777    共享内存的访问权限
                2、对共享内存进行映射或解除映射
                    #include <sys/shm.h>
                    #include <sys/ipc.h>
                    void *shmat(int shmid, const void *shmaddr, int shmflg);
                    int shmdt(const void *shmaddr);
                        参数:shmid-->共享内存id
                             shmaddr -->一般为NULL
                             shmflg -->一般为0
                        返回值:成功返回共享内存的首地址
                                失败 -1
                3、获取、设置共享内存相关属性或删除共享内存
                    #include <sys/shm.h>
                    #include <sys/ipc.h>
                    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
                        参数:cmd -->IPC_STAT //获取共享内存的属性信息
                                  IPC_SET //设置共享内存的属性信息
                                  IPC_RMID //删除共享内存
                             struct shmid_ds -->用来存放共享内存的属性信息
            消息队列
                1、创建消息队列
                    #include <sys/ipc.h>
                    #include <sys/msg.h>
                    int msgget(key_t key, int msgflg);
                        参数:shmflg->IPC_CREAT消息队列不存在则创建
                                      IPC_EXCL消息队列已存在则报错
                                      0777    消息队列的访问权限
                2、收发信息
                    #include <sys/ipc.h>
                    #include <sys/msg.h>
                    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
                        返回值:成功 0 失败 -1
                        参数:msgflg -->默认设置0   //阻塞
                                      IPC_NOWAIT //非阻塞
                             msgp--->要发送数据的存储区域指针
                             msgsz-->要发送数据的大小
                    ssize_t msgrcv(int  msqid,  void *msgp, size_t msgsz, long msgtyp,int msgflg);
                        参数:msgtyp -->你要接收的信息类型
                             msgp--->要接收数据的存储区域指针
                             msgsz-->要接收数据的大小
                    重点:发送消息的格式是有要求的,要求用户自定数据类型
                        struct msgbuf
                        {
                             long msgtype; //表明消息类型
                             char truemsg[50];//真实的信息内容
                        };
                        struct singlelist
                        {                             
                             int num;//真实的数据
                             char buf[10]; //next指针                            
                        };
                3、删除消息队列
                    #include <sys/ipc.h>
                    #include <sys/msg.h>
                    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
                        参数:cmd-->IPC_STAT 获取MSG的信息
                                    IPC_SET  设置MSG的信息
                                    IPC_RMID 删除MSG
                              buf-->存放信息结构体缓冲区
        多线程:由进程生成,资源是进程给的,共享主线程资源,进程退出了,线程就没有了
                1、线程的创建
                    #include <pthread.h>
                    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
                        返回值:成功 0  失败 errno
                        参数:thread -->线程的ID
                             attr -->线程的属性,一般设置为NULL表示使用默认属性
                             void *(*start_routine)(void *) -->线程的功能函数
                             arg -->传递给线程功能函数的参数
                                    万能参数,什么类型都能传递
                2、线程的退出跟回收
                    #include <pthread.h>
                    void pthread_exit(void *retval);  --->在子线程调用
                        参数:retval -->存放线程退出时候的信息
                    int pthread_join(pthread_t thread, void **retval); //--->在主线程调用,回收子线程
                        参数:retval -->接收线程退出时候的信息
                    特点:阻塞主线程,等待子线程退出

                3、线程的取消(干掉线程)
                    #include <pthread.h>
                    int pthread_cancel(pthread_t thread);    //给指定线程发送取消请求
                    int pthread_setcancelstate(int state, int *oldstate); //设置线程的取消状态
                        参数:state --->    PTHREAD_CANCEL_ENABLE //线程默认的取消状态(延时取消)
                                        PTHREAD_CANCEL_DISABLE
                             oldstate -->存放原本的取消状态,一般为NULL
                    int pthread_setcanceltype(int type, int *oldtype); //设置取消类型
                        参数:type -->PTHREAD_CANCEL_DEFERRED //延时取消:当线程被取消的时候,会继续执行到下一个取消点才退出,取消点:指的就是linux规定的一系列函数                           
                                     PTHREAD_CANCEL_ASYNCHRONOUS //立即取消
                4、线程的属性设置
                    #include <pthread.h>
                    第一步:定义属性变量pthread_attr_t,并初始化该变量
                           int pthread_attr_init(pthread_attr_t *attr);
                    第二步:依据你想要设置的某个具体属性选择合适的函数
                           int pthread_attr_setxxxxx();
                    第三步:让你设置的属性生效,通过创建线程实现
                           pthread_create(  传递属性变量)
                    第四步:线程结束,主线程中销毁
                           int pthread_attr_destroy(pthread_attr_t *attr);
                    第一个属性:分离属性
                        分离属性: 可以分离和不可以分离,可以分离意思就是线程不需要主函数去回收,自己回收自己(你强行回收也回收不到)-->自灭                                  
                        int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
                                参数:detachstate -->PTHREAD_CREATE_DETACHED //可分离
                                                    PTHREAD_CREATE_JOINABLE //不可以分离(默认属性)
                    第二个属性:设置优先级
                        SCHED_FIFO --->针对实时线程进程(静态优先级1--99)
                        SCHED_RR   --->分配固定时间片,轮询
                        SCHED_OTHER --->针对非实时线程进程(静态优先级0),按照动态优先级来调度
            互斥锁
                    用来协调多个线程对于共享资源(临界区资源)的访问,多个线程抢同一把锁,先抢到的不会阻塞,后来的线程抢不到锁会阻塞
                    死锁:某个线程上锁之后,没有解锁,导致其他线程无法使用该锁
                1、互斥锁的初始化
                    #include <pthread.h>
                    int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
                        参数:pthread_mutex_t -->互斥锁变量
                             pthread_mutexattr_t -->互斥锁的属性,一般设置为NULL,默认属性
                2、互斥锁的操作
                    #include <pthread.h>
                    int pthread_mutex_lock(pthread_mutex_t *mutex); //阻塞上锁/加锁                 
                    int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞上锁,上锁操作不会阻塞
                    int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
                    特点:多个线程争抢锁的时候,没抢到锁的会阻塞    
                3、处理线程取消时候导致的死锁
                    void pthread_cleanup_push(void (*routine)(void *),void *arg);//注册线程取消时候要执行的函数
                        参数:void (*routine)(void *) -->当线程被取消的时候要执行的函数
                             arg -->传递给routine的参数
                    void pthread_cleanup_pop(int execute);
                        参数:execute --->0表示routine出栈的时候不执行
                                         非0表示routine出栈的时候执行
                    pthread_cleanup_pop(85);//85随便乱写的,非0,当调用该函数,会自动帮助你执行routine
                4、    互斥锁的销毁
                    int pthread_mutex_destroy(pthread_mutex_t *mutex);         
            条件变量:必须配合互斥锁使用
                1、条件变量的初始化
                    int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t * attr);
                        参数:cond -->条件变量
                             attr -->条件变量的属性,一般始终为0
                2、操作条件变量
                    int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);//阻塞条件变量,会先解锁,然后阻塞当前线程                    
                    int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒所有的阻塞在条件变量上线程
                    int pthread_cond_signal(pthread_cond_t *cond);//唤醒阻塞条件变量上某个线程,先解除阻塞,然后自动上锁
                3、条件变量的销毁
                    int pthread_cond_destroy(pthread_cond_t *cond);  
            线程池
                typedef struct task_list    //任务链表
                {
                    void *(*taskfun)(void *argu); //任务函数
                    void *argu;                           //任务参数
                    struct task_list *next;
                }TASK_LIST,*TASK_LIST_POINT;

                typedef struct pthread_pool    //线程池
                {
                    pthread_t  *pid;  //线程ID
                    int task_num;    //线程池中任务个数
                    int pthread_num;    //线程池中线程个数
                    pthread_mutex_t pthread_pool_mutext;  //线程池锁
                    pthread_cond_t  pthread_pool_cond;       //线程池条件变量
                    struct task_list  *task_list_head;            //任务链表表头
                    int pthread_alive_flag;                         //0线程关闭,1打开
                }PTHREAD_POOL,*PTHREAD_POOL_POINT;
                             
                1、任务链表初始化
                    TASK_LIST_POINT task_list_init()   //初始化头结点
                    {
                        TASK_LIST_POINT task_list=malloc(sizeof(TASK_LIST));
                        if(task_list != NULL)
                        {
                            task_list->next = NULL;
                        }
                        return task_list;
                    }
                2、线程从任务链表取出任务并执行
                    void *get_task(void *arg)//线程共用的功能函数:获取任务并执行
                    {
                        PTHREAD_POOL_POINT threadp=(PTHREAD_POOL_POINT)arg;
                        struct task_list *ptask;
                        //从任务链表的头部取出任务然后处理(说白了就是删除节点,处理完了再删除)
                        while(1)
                        {  
                            pthread_mutex_lock(&(threadp->pthread_pool_mutext));//线程拿到任务先上锁
                             
                            while(threadp->task_num<=0&&threadp->pthread_alive_flag==1)//任务数量为0且线程存在
                            {
                                pthread_cond_wait(&(threadp->pthread_pool_cond),&(threadp->pthread_pool_mutext));
                            }
                            if(threadp->task_num<=0&&threadp->pthread_alive_flag==0)//任务数量为0且线程不存在
                            {
                                pthread_mutex_unlock(&(threadp->pthread_pool_mutext));
                                pthread_exit(NULL);
                            }
                             //遍历任务链表,找到head的下一个节点,取出来(删除操作)
                            ptask = threadp->task_list_head->next;
                            threadp->task_list_head->next=ptask->next;
                             
                            threadp->task_num--;             //任务减1
                             
                            pthread_mutex_unlock(&(threadp->pthread_pool_mutext));
                            (ptask->taskfun) (ptask->argu);  //执行任务
                            free(ptask);
                         }
                    }
                3、线程池初始化(包括线程池里线程个数)
                    PTHREAD_POOL_POINT pthread_pool_init(PTHREAD_POOL_POINT mypthread_pool,int pthread_num)//线程初始化
                    {         
                        mypthread_pool->pid=malloc(pthread_num*sizeof(pthread_t));
                        mypthread_pool->pthread_num=pthread_num; 
                        mypthread_pool->task_num=0; 
                        mypthread_pool->task_list_head=malloc(sizeof(TASK_LIST));
                        mypthread_pool->task_list_head->next =NULL;
                        mypthread_pool->pthread_alive_flag=1;
                        pthread_mutex_init(&(mypthread_pool->pthread_pool_mutext),NULL);
                        pthread_cond_init(&(mypthread_pool->pthread_pool_cond),NULL);
                             
                        for(int i=0; i<pthread_num; i++)//创建pthread_num个线程
                        {
                            pthread_create(&mypthread_pool->pid[i],NULL,get_task,mypthread_pool);//创建线程
                        }
                        return mypthread_pool;
                    }  
                4、添加任务
                    void task_add(void *(*sometask)(void *arg),void *somearg,PTHREAD_POOL_POINT mypthread_pool)//添加任务
                    {
                        //找到链表的尾部,封装新的节点,插入到尾部
                        TASK_LIST_POINT  p=malloc(sizeof(TASK_LIST));
                        TASK_LIST_POINT newtask=malloc(sizeof(TASK_LIST));
                        p=mypthread_pool->task_list_head;
                        newtask->taskfun=sometask;
                        newtask->argu=somearg;
                        newtask->next = NULL;
                        pthread_mutex_lock(&(mypthread_pool->pthread_pool_mutext)); 
                        while(p->next != NULL)//操作指针尾部插入
                            p=p->next;
                        p->next=newtask;
                        mypthread_pool->task_num++;
                        pthread_mutex_unlock(&(mypthread_pool->pthread_pool_mutext));
                          
                        pthread_cond_signal(&(mypthread_pool->pthread_pool_cond));//池中有任务时,唤醒线程去处理任务
                    }
                5、线程池的销毁
                    int pthread_pool_destroy(PTHREAD_POOL_POINT mypthread_pool)//销毁线程池
                    {
                        mypthread_pool->pthread_alive_flag=0;
                        pthread_cond_broadcast(&(mypthread_pool->pthread_pool_cond));
                        for(int i=0; i<mypthread_pool->pthread_num; i++)
                        {
                            pthread_join(mypthread_pool->pid[i],NULL);
                        }
                    }
            总结:多进程和多线程的区别
                多进程优点:
                        每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系
                        通过增加CPU,就可以容易扩充性能 
                        可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系
                        每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大
                多进程缺点:
                        逻辑控制复杂,需要和主程序交互
                        需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 
                        多进程调度开销比较大
                多线程优点:
                        无需跨进程边界
                        程序逻辑和控制方式简单 
                        所有线程可以直接共享内存和变量 
                        线程方式消耗的总资源比进程方式好
                多线程缺点:
                        每个线程与主程序共用地址空间,受限于2GB地址空间  
                        线程之间的同步和加锁控制比较麻烦  
                        一个线程的崩溃可能影响到整个程序的稳定性  
                        到达一定的线程数程度后,即使再增加CPU也无法提高性能  
                        线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU

猜你喜欢

转载自blog.csdn.net/lly_3485390095/article/details/83002576
今日推荐