并发程序设计4:多线程

   上一节实现了基于epoll的IO复用并发程序控制,本节记录基于多线程的并发程序设计。

1. 线程和进程

  进程是具有独立功能的程序关于某个数据集合的一次运行活动,是系统资源管理资源分配的基本单位,而线程是进程中代码的一个执行流,是系统调度的基本单位(虽然这句话很常见,但是就用户级线程和内核级线程而言似乎不同,后面会讲到)。同一进程内的线程除了有自己的空间外,还共享进程的资源。线程和进程的区别如图1.1所示。

                                  

                                 (a) 单线程                                                                  (b) 多线程

                                                                  图1.1 进程与线程

可以看到,进程创建时,系统为其分配地址空间,进程有自己的代码段,数据段,堆栈。而线程在创建时,共享进程地址空间,同时维护自己的堆栈。再细分一下,线程可以分为用户级线程(User Level Thread, ULT)和内核级线程(Kernel Level Thread, KLT)。

1.1 KLT和ULT(PS: 此部分仅是操作系统中的概念,用户编程时均是多线程处理)

  不同的操作系统实现线程的方式略有不同,有的操作系统直接支持线程(KLT);而有的操作系统感知不到线程的存在,其线程实现是通过用户空间以库函数来管理实现的(ULT)。

  KLT管理工作由OS来执行,此时OS直接调度线程,进程不再具有多状态(只有挂起和非挂起)。主要特点有:

(1) 进程中一个线程被阻塞,OS调用该进程中其他线程来执行;

(2) 多处理器环境下,内核调用多个线程并行执行(物理并行);

(3) 应用程序在用户模式下进行,线程调度在内核模式下进行。线程调度要模式切换,系统开销大。

  ULT多线程中内核没有意识到线程存在。多线程通过用户空间内的线程库实现。主要特点有:

(1) 线程管理的数据结构在用户空间中,调度不需要模式切换;

(2) 线程调度算法可裁剪和选择(因为是用户库函数来实现的);

(3) 不能利用多处理器优势,OS调用进程,进程中只有一个线程执行。一个线程阻塞,导致整个进程阻塞。

为了解决ULT的线程阻塞导致进程阻塞问题,引入了jacketing技术Jacketing技术可以在线程阻塞时是否进行进程切换转移控制权给其他线程

  此外,还有第三混合式多线程。混合式多线程在用户空间创建多线程ULT(用户创建),同时内核空间创建多线程KLT。将多个ULT映射到(小于等于ULLT的数目)KLT当中。程序员可以调整KLT的数目。KLT的调度由OS负责,ULT的三态由用户调度,并将活跃态的ULT绑定到KLT上。三种方式的对比如图1.2所示。

                          

                                                          图1.2 三种多线程的对比

                          (注:图引自https://www.cnblogs.com/Mered1th/p/10745137.html)

2. Linux下多线程的实现

Linux下多线程实现主要用到了pthread.h库函数,主要函数如下:

int pthread_create(pthread_t* restrict thread, attr, void* (*start_routine)(void *),void* restrict arg);
thread:用于保存线程ID的变量;
attr:属性,设为NULL;
start_routine:线程开始函数,返回值和输入参数都是void指针;
arg:传递给线程的参数

为了使创建的线程结束之后自动销毁,有两种方式:
(1) 由创建线程的进程调用
int pthread_join(pthread_t thread, void **status);
thread:等待的线程ID
status:返回给进程的参数

(2) 线程结束时调用
int pthread_detach(pthread_t thread);

方式很简单,我们仍然以回声服务器服务器端的代码为例,将其变成多线程并发,代码如下:

 1 #include <stdlib.h>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <sys/socket.h>
 5 #include <arpa/inet.h>
 6 #include <unistd.h>
 7 #include <pthread.h>
 8 
 9 void error_handle(const char* msg)
10 {
11     fputs(msg,stderr);
12     fputc('\n',stderr);
13     exit(1);
14 }
15 void* client_thread(void* arg); //线程处理函数
16 
17 int main(int argc,char* argv[])
18 {
19     //服务器建立连接
20     int servsock,clntsock;
21     struct sockaddr_in servaddr,clntaddr;
22     char message[50];
23     socklen_t clntlen=sizeof(clntaddr);;
24     
25     if(argc!=2)
26         error_handle("Please input port number");
27     
28     servsock=socket(PF_INET,SOCK_STREAM,0);  //1.建立套接字
29     
30     memset(&servaddr,0,sizeof(servaddr));
31     servaddr.sin_family=AF_INET;
32     servaddr.sin_addr.s_addr=htonl(INADDR_ANY);  //默认本机IP地址
33     servaddr.sin_port=htons(atoi(argv[1]));  
34     
35     if(bind(servsock,(struct sockaddr*)&servaddr,sizeof(servaddr))==-1)
36         error_handle("bind error");  //2.建立连接
37     
38     if(listen(servsock,10)==-1) //3.监听建立
39         error_handle("listen() error");
40         
41     //到此的代码为socket创建过程,与前三节相同
42     pthread_t t_id; //保存线程ID
43     while(1)
44     {
45         clntsock=accept(servsock,(struct sockaddr*)&clntaddr,&clntlen); //建立客户端连接
46         if(clntsock==-1)
47         {
48             printf("accept() error");
49             close(clntsock);
50         }
51         else{
52         pthread_create(&t_id,NULL,client_thread,(void*)&clntsock);
53         pthread_detach(t_id);
54         printf("connecting");
55         }
56     }
57     
58 }
59 
60 void* client_thread(void* arg)
61 {
62     char message[30];
63     int sock=*((int*)arg);
64     int strlen;
65     while(1)
66     {
67         strlen=read(sock,message,sizeof(message));
68         if(strlen==0)  //断开连接
69         {
70             close(sock);
71             break;
72         }
73         if(strlen>0)
74             write(sock,message,strlen);
75     }
76     
77 }
View Code

ps:Linux下在编译该代码时,应加上线程库,如上面的代码文件名为server.cpp。那么编译命令为:g++ server.cpp -g -o -lpthread

           

猜你喜欢

转载自www.cnblogs.com/yuanwebpage/p/12367710.html