什么是并发:如果逻辑控制流在时间上重叠,那么他就是并发的。
并发主要被看作是一种操作系统内核用来运行多个应用程序的机制。但是并发不仅仅限于内核。
应用级并发的几种应用:
访问慢速IO设备与人交互通过推迟工作以降低延时服务多个网络客户端在多核机器上进行并行计算
现代操作系统提供了三种基本的构造并发程序的方法:进程I/O多路复用线程
基于进程的并发编程
基于进程的并发服务器
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 //#include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 using m_sockaddr_in = struct sockaddr_in; 18 typedef struct sockaddr_in m_sockaddr_in; 19 void sigchld_handler(int sig) 20 { 21 while(waitpid(-1,0,WNOHANG) > 0) 22 ; 23 return; 24 } 25 int open_listenfd( const sockaddr_in& id,const int port) 26 { 27 int socket_id = socket(AF_INET,SOCK_STREAM,0); 28 //id_len = sizeof(id); 29 sockaddr_in server_sock; 30 memset(&server_sock,0,sizeof(server_sock)); 31 server_sock.sin_family = AF_INET; 32 server_sock.sin_addr.s_addr = inet_addr("127.0.0.1"); 33 server_sock.sin_port = htons(1234); 34 bind(socket_id,(sockaddr*)&server_sock,sizeof(server_sock)); 35 listen(socket_id,20); 36 //socklen_t id_size = sizeof(id); 37 //int client_sock = accept(socket_id,(sockaddr*)&id,&id_size); 38 return socket_id; 39 } 40 void echo(int connfd) 41 { 42 std::string str = "hello"; 43 write(connfd,str.c_str(),sizeof(str)); 44 } 45 int main(int argc,char ** argv) 46 { 47 int listenfd,connfd,port; 48 socklen_t clientlen = sizeof(m_sockaddr_in); 49 sockaddr_in clientaddr; 50 if(argc!=2){ 51 fprintf(stderr,"usage:%s n",argv[0]); 52 exit(0); 53 } 54 port = atoi(argv[1]); 55 signal(SIGCHLD,sigchld_handler); 56 listenfd = open_listenfd(clientaddr,port); 57 while(1) 58 { 59 connfd = accept(listenfd,(sockaddr*)&clientaddr,&clientlen); 60 if(fork() == 0) 61 { 62 close(listenfd); 63 echo(connfd); 64 close(connfd); 65 exit(0); 66 } 67 close(connfd); 68 } 69 }
客户端
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 int main(int argc,char*argv[]) 9 { 10 int sock_id = socket(AF_INET,SOCK_STREAM,0); 11 sockaddr_in clientAddr; 12 memset(&clientAddr,0,sizeof(clientAddr)); 13 clientAddr.sin_family = AF_INET; 14 clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 15 clientAddr.sin_port = htons(1234); 16 connect(sock_id,(sockaddr*)&clientAddr,sizeof(clientAddr)); 17 char buf[20] = {0}; 18 read(sock_id,buf,sizeof(buf) - 1); 19 puts("message"); 20 puts(buf); 21 return 0; 22 }
进程的优劣:共享文件表,不共享用户地址空间。
必须显示进行IPC通信。
IO多路复用的基本思路是使用select(epoll)函数,要求内核挂起进程,只有在一个或者多个IO事件发生后,才将控制返回给应用程序。
#include基于线程的并发编程#include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include using m_sockaddr_in = struct sockaddr_in; typedef struct sockaddr_in m_sockaddr_in; void sigchld_handler(int sig) { while(waitpid(-1,0,WNOHANG) > 0) ; return; } int open_listenfd( const sockaddr_in& id,const int port) { int socket_id = socket(AF_INET,SOCK_STREAM,0); //id_len = sizeof(id); sockaddr_in server_sock; memset(&server_sock,0,sizeof(server_sock)); server_sock.sin_family = AF_INET; server_sock.sin_addr.s_addr = inet_addr("127.0.0.1"); server_sock.sin_port = htons(1234); bind(socket_id,(sockaddr*)&server_sock,sizeof(server_sock)); listen(socket_id,20); //socklen_t id_size = sizeof(id); //int client_sock = accept(socket_id,(sockaddr*)&id,&id_size); return socket_id; } void echo(int connfd) { std::string str = "hello"; write(connfd,str.c_str(),sizeof(str)); } void command(){ char buf[100]; if(!fgets(buf,100,stdin)) exit(0); printf("%s",buf); } int main(int argc,char ** argv) { int listenfd,connfd,port; socklen_t clientlen = sizeof(m_sockaddr_in); sockaddr_in clientaddr; fd_set read_set,ready_set; if(argc!=2){ fprintf(stderr,"usage:%s n",argv[0]); exit(0); } port = atoi(argv[1]); sockaddr_in id; listenfd = open_listenfd(id,port); FD_ZERO(&read_set); FD_SET(STDIN_FILENO,&read_set); FD_SET(listenfd,&read_set); while(1){ ready_set = read_set; select(listenfd+1,&ready_set,NULL,NULL,NULL); if(FD_ISSET(STDIN_FILENO,&ready_set)) command(); if(FD_ISSET(STDIN_FILENO,&ready_set)){ connfd = accept(listenfd,(sockaddr*)&clientaddr,&clientlen); echo(connfd); close(connfd); } } }
基于进程的并发编程,每个流使用了单独的进程,流共享数据很困难,第二种创建了自己的逻辑流,只有一个进程,所有流共享一个地址空间。基于线程的,是两种方法的混合。 线程是运行在进程上下文中的逻辑流 。 每个线程都有自己的线程上下文,包括唯一的整数线程ID(thread ID,TID)。栈、栈指针,程序计数器,通用目的的寄存器和条形码。所有的运行在一个进程的线程共享该进程的整个虚拟空间。 每个进程开始生命周期时都是单一线程,这个线程称为主线程。在某一时刻线程创建一个对等线程,从这个时间点开始,两个线程就并发运行。 线程的终止方式: 1当顶层的线程例程返回时,线程会隐式的终止。 2通过调用pthread_exit函数,线程会显示的终止。如果主线程调用pthread_exit,他会等待其他对等线程终止,然后在终止主线程和整个进程,返回值为thread_return. 3某个对等线程调用unix的exit函数,该函数终止进程以及所有与该进程相关的线程。 4另外一个对等线程通过当前线程ID作为参数调用pthread_cancel函数来终止当前线程。
回收终止的线程:
通过调用pthread_join函数等待其他线程终止。pthread_join是阻塞函数,而且只能等待一个指定的线程终止,不同于UNIX中wait函数。分离线程,线程是可结合的或者是分离的。一个可结合的线程能够被其他线程收回其资源和杀死。一个分离的线程不能被其他线程回收或者杀死。他的存储器资源在它终止时由系统自动释放。 一个基于线程的并发服务器
#include#include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include void echo(int connfd); void *thread(void *vargp); int open_listenfd(const int port); int main(int argc,char* argv[]) { int listenfd,*connfdp,port; socklen_t clientlen = sizeof(sockaddr_in); sockaddr_in clientaddr; pthread_t tid; if(argc != 2) { fprintf(stderr,"usage:%s n",argv[0]); exit(0); } port = atoi(argv[1]); listenfd = open_listenfd(port); while(1){ connfdp =(int*) malloc(sizeof(int)); *connfdp = accept(listenfd,(sockaddr*)&clientaddr,&clientlen); pthread_create(&tid,NULL,thread,connfdp); } std::cout << "Hello world" << std::endl; return 0; } void *thread(void *vargp) { int connfd = *((int*)vargp); pthread_detach(pthread_self()); free(vargp); echo(connfd); close(connfd); return NULL; } int open_listenfd(const int port) { int socket_id = socket(AF_INET,SOCK_STREAM,0); //id_len = sizeof(id); sockaddr_in server_sock; memset(&server_sock,0,sizeof(server_sock)); server_sock.sin_family = AF_INET; server_sock.sin_addr.s_addr = inet_addr("127.0.0.1"); server_sock.sin_port = port; bind(socket_id,(sockaddr*)&server_sock,sizeof(server_sock)); listen(socket_id,20); //socklen_t id_size = sizeof(id); //int client_sock = accept(socket_id,(sockaddr*)&id,&id_size); return socket_id; } void echo(int connfd) { std::string str = "hello"; write(connfd,str.c_str(),sizeof(str)); }



