学习整理——多进程和多线程概念理解

进程

一个进程,包括了代码、数据和分配给进程的资源(内存),在计算机系统里直观地说一个进程就是一个PID。操作系统保护进程空间不受外部进程干扰,即一个进程不能访问到另一个进程的内存。有时候进程间需要进行通信,这时可以使用操作系统提供进程间通信机制。通常情况下,执行一个可执行文件操作系统会为其创建一个进程以供它运行。但如果该执行文件是基于多进程设计的话,操作系统会在最初的进程上创建出多个进程出来,这些进程间执行的代码是一样,但执行结果可能是一样的,也可能是不一样的。

为什么需要多进程?最直观的想法是,如果操作系统支持多核的话,那么一个执行文件可以在不同的核心上跑;即使是非多核的,在一个进程在等待I/O操作时另一个进程也可以在CPU上跑,提高CPU利用率、程序的效率。

在Linux系统上可以通过fork()来在父进程中创建出子进程。一个进程调用fork()后,系统会先给新进程分配资源,例如存储数据和代码空间。然后把原来进程的所有值、状态都复制到新的进程里,只有少数的值与原来的进程不同,以区分不同的进程。fork()函数会返回两次,一次给父进程(返回子进程的pid或者fork失败信息),一次给子进程(返回0)。至此,两个进程分道扬镳,各自运行在系统里。

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

void print_exit(){
    printf("the exit pid:%d\n",getpid() );
}  

int main (){
    pid_t pid;
    atexit( print_exit );      //注册该进程退出时的回调函数
    pid=fork();   // new process

    int count = 0;

    if (pid < 0){
        printf("error in fork!");
    }
    else if (pid == 0){
        printf("i am the child process, my process id is %d\n",getpid());
        count ++;
        printf("child process add count.\n");
    }
    else {
        printf("i am the parent process, my process id is %d\n",getpid());

        count++;
        printf("parent process add count.\n");
        sleep(2);
        wait(NULL);
    }  

    printf("At last, count equals to %d\n", count);
   return 0;
} 

上述代码在fork()之后进行了一次判断,以辨别当前进程是父进程还是子进程。

为了说明父进程与子进程有各自的资源空间,设置了对count的计数。Terminal输出如下:、

明显两个进程都执行了count++操作,但由于count是分别处在不同的进程里,所以实质上count在各自进程上只执行了一次。

线程

线程是可执行代码的可分派单元,CPU可单独执行单元。在基于线程的多任务的环境中,所有进程至少有一个线程(主线程),但是它们可以具有多个任务。这意味着单个程序可以并发执行两个或者多个任务。

也就是说,线程可以把一个进程分为很多片,每一片都可以是一个独立的流程,CPU可以选择其中的流程来执行。但线程不是进程,不具有PID,且分配的资源属于它的进程,共享着进程的全局变量,也可以有自己“私有”空间。但这明显不同于多进程,进程是一个拷贝的流程,而线程只是把一条河流截成很多条小溪。它没有拷贝这些额外的开销,但是仅仅是现存的一条河流,就被多线程技术几乎无开销地转成很多条小流程,它的伟大就在于它少之又少的系统开销。

Linux中可以使用pthread库来创建线程,但由于pthread不是Linux内核的默认库,所以编译时需要加入pthread库一同编译。

g++ -o main main.cpp -pthread

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>  

void* task1(void*);
void* task2(void*);  

void usr();
int p1,p2;  

int count = 0 ;

int main()  {
    usr();
    return 0;
}  

void usr(){
    pthread_t pid1, pid2;
    pthread_attr_t attr;
    void *p1, *p2;
    int ret1=0, ret2=0;
    pthread_attr_init(&attr);                                                  //初始化线程属性结构
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);   //设置attr结构
    pthread_create(&pid1, &attr, task1, NULL);            //创建线程,返回线程号给pid1,线程属性设置为attr的属性
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&pid2, &attr, task2, NULL);            

    ret1=pthread_join(pid1, &p1);         //等待pid1返回,返回值赋给p1
    ret2=pthread_join(pid2, &p2);         //等待pid2返回,返回值赋给p2
    printf("after pthread1:ret1=%d,p1=%d\n", ret1,(int)p1);
    printf("after pthread2:ret2=%d,p2=%d\n", ret2,(int)p2); 

    printf("At last, count equals to %d\n", count);
}  

void* task1(void *arg1){
    printf("task1 begin.\n");
    count++;
    printf("task1 thread add count.\n");
    pthread_exit( (void *)1);
}  

void* task2(void *arg2){
    printf("thread2 begin.\n");
    count ++;
    printf("task2 thread add count.\n");
    pthread_exit((void *)2);
} 

上述代码的中主线程在usr()函数中创建了两个线程,线程的属性都是JOINABLE,即可以被其他线程收集返回信息。然后等待两个线程的返回,输出返回信息。

Terminal的输出显示thread2先于thread1执行,表明了这不是一个同步的程序,线程的运行是单独进行的,由内核线程调度来进行的。为了区别进程,在代码中也加入了count++操作。最后在主线程中输出count=2,即count被计数了2次,子线程被允许使用同一个进程内的共享变量,区别了进程的概念。

由于线程顺序、时间的不确定性,往往需要对进程内的一个共享变量进行读写限制,比如加锁等。

总结

多进程和多线程的概念可以概括为一条河流。

1.纵向的是多线程,进程就像河流,线程就是分流,线程从一个进程中衍生出来。河流可以把水引向分流分担自身的流水量,且各个分流是同时流水的(并发),它们共享了一个源头(共享变量);

2.横向的是多进程,一条河流从源头上几乎完全与另一条河流一样,但由于一些环境的不同,造成了两者最后的流向、各个分流的流量可能会不一样,但是两条河流同时在流水(并发)。

时间: 2024-10-16 07:11:02

学习整理——多进程和多线程概念理解的相关文章

多进程和多线程的理解,通俗易懂

通俗易懂,适合初学者阅读.强烈推荐.5+星. 申明:此文章是在他处看到的,转自阮一峰博客. 来源:  http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html 1. 计算机的核心是CPU,它承担了所有的计算任务.它就像一座工厂,时刻在运行. 2. 假定工厂的电力有限,一次只能供给一个车间使用.也就是说,一个车间开工的时候,其他车间都必须停工.背后的含义就是,单个CPU一次只能运行一个任务. 3. 进程就好比工厂的车间,它代

【Python 学习】多进程和多线程

一.多进程 linux平台例子: Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回. 子进程永远返回0,而父进程返回子进程的ID.这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID. import os,ran

[转]论多进程与多线程

出于对自己对多进程和多线程概念理解的怀疑,便花时间深入学习了一下.我的目的是将一个生动的围绕CPU运行的动作模型描述出来. 我们先看专业书上是怎么解释的--进程是资源分配的最小单位,线程是CPU调度的最小单位--.只要能把这句话理解了,那也就对多进程与多线程理解的差不多了. 我们来看一下操作系统是怎么协调程序利用单核CPU,内存来运行的:因为是单核的,原则上讲应该是执行一段线程,再切换到令一个线程,是只能这样,但是操作系统是怎么做使得看起来多个程序同时运行的呢?操作系统给每一个程序的运行实体--

[Android学习笔记]Android中多线程开发的一些概念

线程安全: 在多线程的情况下,不会因为线程之间的操作而导致数据错误. 线程同步: 同一个资源,可能在同一时间被多个线程操作,这样会导致数据错误.这是一个现象,也是一个问题,而研究如何解决此类问题的相关工作就叫做线程同步. android中,处理线程同步的手段就是:锁 一般分为公平锁和非公平锁: synchronized(内部锁,互斥锁):synchronized是JVM提供的线程同步机制,如果出现问题,JVM能捕获异常,并释放资源,具体实现机制需要查看JVM源码 synchronized的使用特

对于多线程概念的理解

昨天晚上兴致大好,故又捧起java书,随意的翻动了几页. 多线程的概念之前总觉得晦涩难懂,昨天看到讲它的章节,却忽然理解了一些. 多线程,其实是多,线程的组合.线程,线性的程序.线性,上过学的大家想必都不难理解其概念.我理解为单向性(当然有方向).故多线程可以理解为多个单向性程序.结合现实生活,就可以理解为一件不可拆分的事情. 多线程的出现就是为了解决同时干多件事的情况.那它的工作原理是什么呢? 比如你想同时看三本书,先看第一本的第一章,再去看第二本的第一章,再去看第三本的第一章,然后再回去看第

(转载)Java多线程入门理解

转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢?如果你觉得此文很简单,那推荐你看看Java并发包的的线程池(Java并发编程与技术内幕:线程池深入理解),或者看这个专栏:Java并发编程与技术内幕.你将会对Java里头的高并发场景下的线程有更加深刻的理解. 目录(?)[-] 一扩展javalangThread类 二实现javalan

Spring?IOC设计原理解析:本文乃学习整理参考而来

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

python进阶学习(一)--多线程编程

1. 多线程 概念:简单地说操作系统可以同时执行多个不用程序.例如:一边用浏览器上网,一边在听音乐,一边在用笔记软件记笔记. 并发:指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务"一起"执行(实际上总有一些任务不在执行,因为切换任务的熟度相当快,看上去一起执行而已) 并行:指的是任务数小于等于CPU核数,即任务真的是一起执行的. 2. 线程 概念:线程是进程的一个实体,是CPU调度和分派的基本单位. threading--单线程执行: 1 import ti

多进程与多线程差别

 在Unix上编程採用多线程还是多进程的争执由来已久,这样的争执最常见到在C/S通讯中服务端并发技术 的选型上,比方WEBserver技术中.Apache是採用多进程的(perfork模式,每客户连接相应一个进程,每进程中仅仅存在唯一一个运行线程), Java的Web容器Tomcat.Websphere等都是多线程的(每客户连接相应一个线程,全部线程都在一个进程中). 从Unix发展历史看,伴随着Unix的诞生进程就出现了.而线程非常晚才被系统支持,比如Linux直到内核2.6.才支持符合P