多线程与多进程的区别

上传人:ji****72 文档编号:37693501 上传时间:2018-04-21 格式:DOCX 页数:7 大小:23.85KB
返回 下载 相关 举报
多线程与多进程的区别_第1页
第1页 / 共7页
多线程与多进程的区别_第2页
第2页 / 共7页
多线程与多进程的区别_第3页
第3页 / 共7页
多线程与多进程的区别_第4页
第4页 / 共7页
多线程与多进程的区别_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《多线程与多进程的区别》由会员分享,可在线阅读,更多相关《多线程与多进程的区别(7页珍藏版)》请在金锄头文库上搜索。

1、进程: 子进程是父进程的复制品.子进程获得父进程数据空间、堆和栈的复制品。 线程:相对与进程而言,线程是一个更接近与执行体的概念,它可以与同进程的其他线程 共享数据,但拥有自己的栈空间,拥有独立的执行序列。根本区别:用多进程每个进程有自己的地址空间,线程则共享地址空间。所以其他区别都 是由此而来的: 1、 速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间 内。 2、 资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。 3、 同步问题:线程使用 公共变量/内存 时需要使用同步机制还是因为他们在同一个地址 空间内。很想写点关于多进程和多线程的东西,我确实很

2、爱他们。但是每每想动手写点关于他们的 东西,却总是求全心理作祟,始终动不了手。 今天终于下了决心,写点东西,以后可以再修修补补也无妨。一.为何需要多进程(或者多线程),为何需要并发? 这个问题或许本身都不是个问题。但是对于没有接触过多进程编程的朋友来说,他们确实 无法感受到并发的魅力以及必要性。 我想,只要你不是整天都写那种 int main()到底的代码的人,那么或多或少你会遇到代码响 应不够用的情况,也应该有尝过并发编程的甜头。就像一个快餐点的服务员,既要在前台 接待客户点餐,又要接电话送外卖,没有分身术肯定会忙得你焦头烂额的。幸运的是确实 有这么一种技术,让你可以像孙悟空一样分身,灵魂出

3、窍,乐哉乐哉地轻松应付一切状况, 这就是多进程/线程技术。 并发技术,就是可以让你在同一时间同时执行多条任务的技术。你的代码将不仅仅是从上 到下,从左到右这样规规矩矩的一条线执行。你可以一条线在 main 函数里跟你的客户交流, 另一条线,你早就把你外卖送到了其他客户的手里。所以,为何需要并发?因为我们需要更强大的功能,提供更多的服务,所以并发,必不可 少。二.多进程 什么是进程。最直观的就是一个个 pid,官方的说法就:进程是程序在计算机上的一次执行 活动。 说得简单点,下面这段代码执行的时候 cpp view plaincopyprint? int main() printf(”pid i

4、s %d/n”,getpid() ); return 0; 进入 main 函数,这就是一个进程,进程 pid 会打印出来,然后运行到 return,该函数就退 出,然后由于该函数是该进程的唯一的一次执行,所以 return 后,该进程也会退出。看看多进程。linux 下创建子进程的调用是 fork();cpp view plaincopyprint? #include #include #include void print_exit() printf(“the exit pid:%d/n“,getpid() ); main () pid_t pid; atexit( print_exit

5、); /注册该进程退出时的回调函数 pid=fork(); if (pid #include #include #include #include void* task1(void*); void* task2(void*); void usr(); int p1,p2; int main() usr(); getchar(); return 1; void usr() pthread_t pid1, pid2; pthread_attr_t attr; void *p; int ret=0; pthread_attr_init( /初始化线程属性结构 pthread_attr_setdeta

6、chstate( /设置 attr 结构为分 离 pthread_create( /创建线程,返回线程号给 pid1,线程属性 设置为 attr 的属性,线程函数入口为 task1,参数为 NULL pthread_attr_setdetachstate( pthread_create( /前台工作 ret=pthread_join(pid2, /等待 pid2 返回,返回值赋给 p printf(“after pthread2:ret=%d,p=%d/n“, ret,(int)p); void* task1(void *arg1) printf(“task1/n“); /艰苦而无法预料的工作

7、,设置为分离线程,任其自生自灭 pthread_exit( (void *)1); void* task2(void *arg2) int i=0; printf(“thread2 begin./n“); /继续送外卖的工作 pthread_exit(void *)2); 这个多线程的例子应该很明了了,主线程做自己的事情,生成 2 个子线程,task1 为分离, 任其自生自灭,而 task2 还是继续送外卖,需要等待返回。(因该还记得前面说过僵尸进 程吧,线程也是需要等待的。如果不想等待,就设置线程为分离线程)额外的说下,linux 下要编译使用线程的代码,一定要记得调用 pthread 库。

8、如下编译:gcc -o pthrea -pthread pthrea.c四.比较以及注意事项1.看完前面,应该对多进程和多线程有个直观的认识。如果总结多进程和多线程的区别, 你肯定能说,前者开销大,后者开销较小。确实,这就是最基本的区别。 2.线程函数的可重入性: 说到函数的可重入和线程安全,我偷懒了,引用网上的一些总结。线程安全:概念比较直观。一般说来,一个函数被称为线程安全的,当且仅当被多个并发 线程反复调用时,它会一直产生正确的结果。可重入:概念基本没有比较正式的完整解释,但是它比线程安全要求更严格。根据经验, 所谓“重入”,常见的情况是,程序执行到某个函数 foo()时,收到信号,于是

9、暂停目前正在 执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到 刚刚执行的函数 foo(),这样便发生了所谓的重入。此时如果 foo()能够正确的运行,而且处 理完成后,之前暂停的 foo()也能够正确运行,则说明它是可重入的。 线程安全的条件: 要确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的不同线程 会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器。因此, 对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态 变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,

10、则必须通过加锁的方式。 可重入的判断条件: 要确保函数可重入,需满足一下几个条件: 1、不在函数内部使用静态或全局数据2、不返回静态或全局数据,所有数据都由函数的调用者提供。 3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。 4、不调用不可重入函数。可重入与线程安全并不等同,一般说来,可重入的函数一定是线程安全的,但反过来不一 定成立。它们的关系可用下图来表示:比如:strtok 函数是既不可重入的,也不是线程安全的;加锁的 strtok 不是可重入的,但线 程安全;而 strtok_r 既是可重入的,也是线程安全的。如果我们的线程函数不是线程安全的,那在多线程调用的情况下,可

11、能导致的后果是显而 易见的共享变量的值由于不同线程的访问,可能发生不可预料的变化,进而导致程序 的错误,甚至崩溃。3.关于 IPC(进程间通信进程间通信) 由于多进程要并发协调工作,进程间的同步,通信是在所难免的。 稍微列举一下 linux 常见的 IPC. linux 下进程间通信的几种主要手段简介: 管道(管道(Pipe)及有名管道()及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名 管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关 系进程间的通信; 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除

12、了用 于进程间通信外,进程还可以发送信号给进程本身;linux 除了支持 Unix 早期信号语义函数 sigal 外,还支持语义符合 Posix.1 标准的信号函数 sigaction(实际上,该函数是基于 BSD 的, BSD 为了实现可靠信号机制,又能够统一对外接口,用 sigaction 函数重新实现了 signal 函 数); 报文(Message)队列(消息队列):消息队列是消息的链接表,包括 Posix 消息队列 system V 消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可 以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以

13、及缓冲区大小受限等缺点。 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用 IPC 形式。是针对其他 通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程 间的同步及互斥。 信号量(信号量(semaphore):):主要作为进程间以及同一进程不同线程之间的同步手段。 套接口(套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起 初是由 Unix 系统的 BSD 分支开发出来的,但现在一般可以移植到其它类 Unix 系统上: Linux 和 System V 的变种都支持套接字。 或许你会有疑问,那多线程间要通信,应该怎么做?前

14、面已经说了,多数的多线程都是在 同一个进程下的,它们共享该进程的全局变量,我们可以通过全局变量全局变量来实现线程间通信。 如果是不同的进程下的 2 个线程间通信,直接参考进程间通信。4.关于线程的堆栈 说一下线程自己的堆栈问题。是的,生成子线程后,它会获取一部分该进程的堆栈空间,作为其名义上的独立的私有空 间。(为何是名义上的呢?)由于,这些线程属于同一个进程,其他线程只要获取了你私有 堆栈上某些数据的指针,其他线程便可以自由访问你的名义上的私有空间上的数据变量。 (注:而多进程是不可以的,因为不同的进程,相同的虚拟地址,基本不可能映射到相同 的物理地址)5.在子线程里 fork看过好几次有人

15、问,在子线程函数里调用 system 或者 fork 为何出错,或者 fork 产生的子 进程是完全复制父进程的吗? 我测试过,只要你的线程函数满足前面的要求,都是正常的。cpp#include #include #include #include #include void* task1(void *arg1) printf(“task1/n“); system(“ls“); pthread_exit( (void *)1); int main() int ret=0; void *p; int p1=0; pthread_t pid1; pthread_create( ret=pthread_join(pid1, printf(“end main/n“); return 1; 上面这段代码就可以正常得调用 ls 指令。不过,在同时调用多进程(子进程里也调用线程函数)和多线程的情况下,函数体内很有 可能死锁。 具体的例子可以看看这篇文章。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号