Linux并发服务器编程之多线程并发服务器
来源:互联网 发布:windows mobile 编辑:程序博客网 时间:2024/06/06 09:44
转载:http://blog.csdn.net/qq_29227939/article/details/53782198
上一篇文章使用fork函数实现了多进程并发服务器,但是也提到了一些问题:
- fork是昂贵的。fork时需要复制父进程的所有资源,包括内存映象、描述字等;
- 目前的实现使用了一种写时拷贝(copy-on-write)技术,可有效避免昂贵的复制问题,但fork仍然是昂贵的;
- fork子进程后,父子进程间、兄弟进程间的通信需要进程间通信IPC机制,给通信带来了困难;
- 多进程在一定程度上仍然不能有效地利用系统资源;
- 系统中进程个数也有限制。
下面就介绍实现并发服务器的另外一种方式,使用多线程实现。多线程有助于解决以上问题。
线程基础
关于线程的概念就不介绍了,先了解一下linux下线程的一些基本操作。
线程基础函数
- pthread_create 创建线程
pthread_create 函数用于创建新线程。当一个程序开始运行时,系统产生一个称为初始线 程或主线程的单个线程。额外的线程需要由 pthread_create 函数创建。 pthread_create 函数原型如下:
- 1
- 2
如果新线程创建成功,参数 tid 返回新生成的线程 ID。一个进程中的每个线程都由一个 线程 ID 标识,其类型为 pthread_t。attr 指向线程属性的指针。每个线程有很多属性包括:优 先级、起始栈大小、是否是守护线程等等。通常将 attr 参数的值设为 NULL,这时使用系统 默认的属性。
但创建完一个新的线程后,需要说明它将执行的函数。函数的地址由参数 func 指定。该函数必须是一个静态函数,它只有一个通用指针作为参数,并返回一个通用指针。该执行函 数的调用参数是由 arg 指定,arg 是一个通用指针,用于往 func 函数中传递参数。如果需要传递多个参数时,必须将它们打包成一个结构,然后让 arg 指向该结构。线程以调用该执行 函数开始。
如果函数调用成功返回 0,出错则返回非 0。
常见的返回错误值:
- 1
- 2
- 3
示例代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
给线程传递参数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
pthread_join
看这个函数首先提出一个概念,线程的类型。线程分为两类:可联合的和分离的。- 默认情况下线程都是可联合的。可联合的线程终止 时,其线程 ID 和终止状态将保留,直到线程调用 pthread_join 函数。
- 而分离的线程退出后, 系统将释放其所有资源,其他线程不能等待其终止。如果一个线程需要知道另一个线程什么 时候终止,最好保留第二个线程的可联合性。
pthread_join 函数与进程的 waitpid 函数功能类似,等待一个线程终止。
pthread_join 函数原型如下:
- 1
- 2
参数 tid 指定所等待的线程 ID。该函数必须指定要等待的线程,不能等待任一个线程结束。要求等待的线程必须是当前进程的成员,并且不是分离的线程或守护线程。
几个线程不 能同时等待一个线程完成,如果其中一个成功调用 pthread_join 函数,则其他线程将返回 ESRCH 错误。
如果等待的线程已经终止,则该函数立即返回。如果参数 status 指针非空,则 指向终止线程的退出状态值。
该函数如果调用成功则返回 0,出错时返回正的错误码。
- pthread_detach
pthread_detach 函数将指定的线程变成分离的。 pthread_detach 函数原型如下:
- 1
- 2
参数 tid 指定要设置为分离的线程 ID。
- pthread_self
每一个线程都有一个 ID,pthread_self 函数返回自己的线程 ID。 pthread_self 函数原型如下:
- 1
- 2
参数 tid 指定要设置为分离的线程 ID。 函数返回调用函数的线程 ID。
例如,线程可以通过如下语句,将自己设为可分离的:
- 1
- pthread_exit
函数 pthread_exit 用于终止当前线程,并返回状态值,如果当前线程是可联合的,则其 退出状态将保留。 pthread_exit函数原型如下:
- 1
- 2
参数 status 指向函数的退出状态。这里的 status 不能指向一个局部变量,因为当前线程 终止后,其所有局部变量将被撤销。
该函数没有返回值。
还有两种方法可以使线程终止:
- 启动线程的函数 pthread_create 的第三个参数返回。该返回值就是线程的终止状态。
- 如果进程的 main 函数返回或者任何线程调用了 exit 函数,进程将终止,线程将随之 终止。
下面可以看一下多线程并发服务器的实例了,需要注意的是,线程建立后,父、子线程不需要关闭任何的描述符,因为线程中使用的描述符是共享进程中的数据。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
线程安全性
上面的示例代码服务器端的业务逻辑都比较简单,没有涉及到共享数据产生的同步问题。在某些情况下,我们需要多个线程共享全局数据,在访问这些数据时就需要用到同步锁机制。而在共享线程内的全局数据时,可以使用Linux提供的线程特定数据TSD解决。
同步机制
在linux系统中,提供一种基本的进程同步机制—互斥锁,可以用来保护线程代码中共享数据的完整性。
操作系统将保证同时只有一个线程能成功完成对一个互斥锁的加锁操作。
如果一个线程已经对某一互斥锁进行了加锁,其他线程只有等待该线程完成对这一互斥锁解锁后,才能完成加锁操作。
互斥锁函数
- 1
参数说明:
- 1
- 2
- 3
锁定成功返回0,否则返回错误码。
- 1
用于互斥锁解锁操作。成功返回0,否则返回错误码。
示例代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
线程私有数据
在多线程环境里,应避免使用静态变量。在 Linux 系统中提供 了线程特定数据(TSD)来取代静态变量。它类似于全局变量,但是,是各个线程私有的, 它以线程为界限。TSD 是定义线程私有数据的惟一方法。同一进程中的所有线程,它们的同 一特定数据项都由一个进程内惟一的关键字 KEY 来标志。用这个关键字,线程可以存取线程私有数据。 在线程特定数据中通常使用四个函数。
- pthread_key_create
- 1
- 2
pthread_key_create 函数在进程内部分配一个标志 TSD 的关键字。
参数 key 指向创建的关 键字,该关键字对于一个进程中的所有线程是惟一的。所以在创建 key 时,每个进程只能调 用一次创建函数 pthread_key_create。在 key 创建之前,所有线程的关键字值是 NULL。一旦 关键字被建立,每个线程可以为该关键字绑定一个值。这个绑定的值对于线程是惟一的,每 个线程独立维护。
参数 destructor 是一个可选的析构函数,可以和每个关键字联系起来。如果一个关键字 的 destructor 函数不为空,且线程为该关键字绑定了一个非空值,那么在线程退出时,析构函 数将会被调用。对于所有关键字的析构函数,执行顺序是不能指定的。
该函数正常执行后返回值为 0,否则返回错误码。
- pthread_once
- 1
- 2
pthread_once 函数使用 once 参数所指的变量,保证每个进程只调用一次 init 函数。通常 once 参数取常量 PTHREAD_ONCE_INIT,它保证每个进程只调用一次 init 函数。
该函数正常执行后返回值为 0,否则返回错误码。
- pthread_setspecific
- 1
- 2
pthread_setspecific 函数为 TSD 关键字绑定一个与本线程相关的值。
参数 key 是 TSD 关 键字。
参数 value 是与本线程相关的值。value 通常指向动态分配的内存区域。
该函数正常执行后返回值为 0,否则返回错误码。
- pthread_getspecific
- 1
- 2
pthread_getspecific 函数获取与调用线程相关的 TSD 关键字所绑定的值。
参数 key 是 TSD 关键字。
该函数正常执行后返回与调用线程相关的 TSD 关键字所绑定的值。否则返回 NULL。
线程安全性代码示例:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
小结
使用多线程实现并发服务器的优点是线程的开销小,切换容易。但是由于线程共享相同 的内存区域,所以在对共享数据的进行操作时,要注意同步问题。其中线程特定数据虽然实现起来比较烦琐,但是它是将一个非线程安全 函数转换成线程安全函数的常用方法。
除此之外,还可以通过改变调用函数参变量的方式实现线程的安全性,这里不作介绍。
下一篇文章将介绍另外一种实现并发服务器的方法:I/O 多路复用。
- Linux并发服务器编程之多线程并发服务器
- Linux并发服务器编程之多线程并发服务器
- 高并发服务器编程之多线程并发服务器
- Linux并发服务器编程之多进程并发服务器
- 高并发服务器编程之多进程并发服务器
- Linux多线程并发服务器编程(线程池,FTP服务器)
- Linux之并发线程服务器
- 线程实现并发服务器
- Linux TCP 服务器编程(六):基于线程的并发服务器
- 并发服务器编程
- 多线程并发服务器编程
- 多线程并发服务器编程
- 多线程并发服务器编程
- linux网络编程多进程并发服务器
- linux网络编程多进程并发服务器
- linux网络编程:并发服务器的模型
- Linux网络编程之高级并发服务器
- Linux网络编程之简单并发服务器
- 二叉树实现先序遍历(迭代版)
- 四. 正则表达式的使用
- GIT本地删除除master以外所有分支
- Leetcode Swap Nodes in Pairs
- linux 下 *.so动态库引入
- Linux并发服务器编程之多线程并发服务器
- 4. 工厂模式
- 关于QT的 常量中有换行符 的问题
- java面试题及答案
- 【Leetcode】252,253 meeting room系列 区间问题
- UVA
- Python之——Eclipse +PyDev开发python,import其他模块时eclipse会报错,运行却没有问题
- Lua泛型for
- Codeforces 869C ( Codeforces Round #439 (Div. 2) ) The Intriguing Obsession 组合数学