您的位置:首页 > 编程语言

并发编程

2015-07-31 00:31 302 查看
并发:如果两个程序的逻辑流在时间上有重叠,我们称这两个程序是并发的。

并发分为两类:内核级并发和应用级并发。

内核级并发:操作系统用来同时运行多个应用程序、实现多任务的机制。

应用级并发:应用程序同时完成多个功能叫做应用级并发,使用应用级并发的程序称为并发程序。

操作系统提供了三种构造并发程序的方法:

进程:每个逻辑控制流都是一个进程,由内核调度和维护,进程有自己独立的虚拟内存空间,与其他流通信需要进程间通信进制支持。

I/O多路复用:应用程序在一个进程的上下文中显式地调度它们自己的逻辑流。因为程序是一个单独的进程,所以所有流共享同一个地址空间。

线程:线程是运行在单一进程上下文中的逻辑流,因此所有流共享同一个虚拟地址空间,但是它由内核调度。

一、基于进程的并发编程

构造并发程序最简单的方法就是用进程,使用fork、exec和waitpid等函数实现进程控制。事实上,由fork函数产生的子进程和父进程就是并发运行的。

使用进程进行并发编程的优点:由于每个进程有自己独立的地址空间,所以一个进程不可能会不小心覆盖另一个进程的虚拟存储器。

缺点:进程间共享状态信息(通信)非常困难;而且进程速度很慢,因为进程控制和IPC(进程间通信)的开销很高。

二、基于I/O多路复用的并发编程

I/O多路复用技术基本思路是使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。

实例代码:

port = atoi(argv[1]);
listenfd = Open_listenfd(port);//一个监听描述符
FD_ZERO(&read_set);//创建一个空的读集合
FD_SET(STDIN_FILENO,  &read_set);//在读集合中增加描述符0(标准输入)
FD_SET(listenfd, &read_set);//在读集合中增加描述符3(监听描述符)
ready_set = read_set;
Select(listenfd+1, &ready_set, NULL, NULL, NULL);//调用select函数,此函数会一直阻塞,直到标准输入或监听描述符准备好已读
if(FD_ISSET(STDIN_FILENO, &ready_set))//判断具体是哪一个描述符准备好已读,调用相应代码
{}
if(listenfd, &ready_set))
{}
I/O多路复用技术的优点:通过显式地调度流,给了程序员更多的对程序行为的控制方法;此外,由于其是运行在单一进程上下文中的,因此每个逻辑流都能访问该进程的全部地址空间,这使得在流之间共享数据变得很容易。

缺点:编码复杂,不能充分利用多核处理器。
三、基于线程的并发编程

线程是运行在进程上下文中的逻辑流,是上述两种方法的混合。

同进程一样,线程由内核自动调度,并且内核通过一个整数ID来识别线程。同基于I/O多路复用的流一样,多个线程运行在单一进程的上下文中,因此共享这个进程虚拟地址空间的整个内容。

多线程的执行模型和多进程的执行模型是相似的,它们都有自己的上下文,线程/进程切换时需要进行上下文切换。

线程执行和进程执行的不同:线程的上下文切换要比进程快得多;线程不是按照严格的父子层次来组织的,和一个进程相关的线程组成一个对等的线程池,主线程和其他线程的唯一区别在于它是进程中第一个运行的线程,一个线程可以杀死它的任意对等线程,或者等待它的任意对等线程终止。

Posix线程是在C语言中处理线程的一个标准接口。示例:

#include "csapp.h"
void *thread(void *vargp);
int main()
{
pthread_t tid;
Pthread_create(&tid, NULL, thread, NULL);
Pthread_join(tid, NULL);
exit(0);
}
void *thread(void *vargp)
{
printf("Hello,world!\n");
return NULL;
}
1、创建线程

线程通过调用pthread_create函数创建其他线程。

int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg);//若成功则返回0,失败则返回非0
其中tid在调用结束后保存新创建线程的ID,attr用来改变创建线程的默认属性,f是新创建线程的入口地址,arg是新创建线程的输入变量。

2、终止线程

a)、顶层的线程返回时,线程会隐式地终止。

b)、通过调用pthread_exit函数,线程会显式地终止。

c)、某个对的线程调用Unix的exit函数,该函数终止进程以及所有与该进程相关的线程。

d)、一个线程可以通过使用线程参数ID调用pthread_cancle函数来终止相应线程。

3、回收已终止线程的资源

线程通过pthread_join函数等待其他线程终止并回收已终止线程占用的所有存储器资源。

int pthread_join(pthread_t tid, void **thread_return);//若成功则返回0,若出错则为非零
注意:与进程不同,pthread_join函数只能等待一个指定的线程终止。
4、分离线程

可结合线程:能够被其他线程收回其资源和杀死。

分离的线程:不能被其他线程回收或杀死,它的存储器资源在它终止时由系统自动释放。

线程默认为可结合的,为了避免存储器泄露,每个可结合线程都应该被其他线程显示地收回,或者通过pthread_detach函数来被分离。

5、初始化线程

pthread_once函数允许初始化与线程例程相关的状态。

int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: