由于Web服务器端协议本身具有的特点,经常需要同时向多个客户端提供服务。因此,人们逐渐舍弃进程。
1、创建线程
函数 pthread_create()
#include <pthread.h>
int pthread_create( pthread_t *thread, const pthread_attr_t *attr,
void*( *start_routine)(void *), void *arg );
功能:
创建一个线程
参数:
thread:保存新创建线程ID的地址;
attr: NULL 表示创建一个线程;
start_routine 函数指针,在线程里运行的操作;
arg:函数指针的参数(地址值)。 传入的,可以在函数里用
返回值:
成功,返回0;失败,返回其它值。
函数 pthread_join()
#include <pthread.h>
int pthread_join( pthread_t thread, void **status );
功能:
调用该函数时,进程(或线程)会进入等待状态,直到第一个参数ID(thread)的线程终止为止(线程会销毁)。有了这个函数后就不用 sleep()让进程先睡眠, 等线程结束后再工作了(因为有时候无法预估线程的操作时间)。
【问题】线程终止前,调用该函数的线程将进入阻塞状态。(所以用的更多的还是 pthread_detach()函数)
参数:
thread:该参数值ID的线程终止后才会从该函数返回;
status:创建线程 thread时,线程中进行的函数的返回值。(应为 void *型,用的时候自己要创建指针变量,然后记得 free() )
返回值:
成功,返回0;失败,返回其它值。
2、互斥量
用于解决多线程同步问题。加锁就vans!
互斥量的创建与销毁
函数 pthread_mutex_init() 创建互斥量
#include <pthread.h>
int pthread_mutex_init( pthread_mutex_t * mutex, const pthread_mutexattr_t *attr);
功能:
互斥量的创建。(不推荐使用宏的方法)
使用宏的方法:(要在全局变量的时候定义)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
参数:
mutex:创建互斥量时传递保存互斥量的变量的地址值。(最好定义成全局变量);
attr:传递即将创建的互斥量属性,一般写 NULL。
返回值:
成功,返回0; 失败,返回其它值。
函数 pthread_mutex_destroy() 销毁互斥量
#include <pthread.h>
int pthread_mutex_destroy( pthread_mutex_t *mutex );
功能:
销毁互斥量
参数:
mutex:创建互斥量时传递保存互斥量的变量的地址值。(最好定义成全局变量);
返回值:
成功,返回0; 失败,返回其它值。
加锁、解锁
函数 pthread_mutex_lock() 加锁
#include <pthread.h>
int pthread_mutex_lock( pthread_mutex_t *mutex );
功能:
上锁
参数:
mutex:创建互斥量时传递保存互斥量的变量的地址值。(最好定义成全局变量);
返回值:
成功,返回0; 失败,返回其它值。
函数 pthread_mutex_unlock() 加锁
#include <pthread.h>
int pthread_mutex_unlock( pthread_mutex_t *mutex );
功能:
解锁;
参数:
mutex:创建互斥量时传递保存互斥量的变量的地址值。(最好定义成全局变量);
返回值:
成功,返回0; 失败,返回其它值。
3、信号量
信号量的创建 与 销毁
函数 sem_init(); 创建信号量
#include <semaphore.h>
int sem_init( sem_t *sem, int pshared, unsigned int value );
功能:
创建信号量;
参数:
sem:创建信号量时传递保存信号量的变量地址值;
pshared:一般写 0;
value:指定新创建的信号量初始值。
返回值:
成功,返回0; 失败,返回其它值。
函数 sem_destroy(); 销毁信号量
#include <semaphore.h>
int sem_destroy( sem_t *sem );
功能:
销毁信号量;
参数:
sem:销毁信号量时传递保存信号量的变量地址值;
返回值:
成功,返回0; 失败,返回其它值。
信号量的 增、减
函数 sem_post() 信号量增
#include <semaphore.h>
int sem_post( sem_t *sem );
功能:
信号量 增;
参数:
sem:传递保存信号量读取值得地址, sem_post()时信号量增1,sem_wait()时信号量减1;
返回值:
成功,返回0;失败,返回其它值。
函数 sem_wait () 信号量减
#include <semaphore.h>
int sem_wait ( sem_t *sem );
功能:
信号量 减;
参数:
sem:传递保存信号量读取值得地址,sem_post()时信号量增1,sem_wait()时信号量减1;
返回值:
成功,返回0;失败,返回其它值。
4、线程得销毁和多线程并发服务器端的实现
函数 pthread_detach() 销毁线程
#include <pthread.h>
int pthread_detach( pthread_t thread );
功能:
销毁线程。
之前调用过pthread _join()函数。调用该函数时,不仅会等待线程终止,还会引导线程销毁。但该函数的问题是,线程终止前,调用该函数的线程将进人阻塞状态。
因此,通常通过pthread_detach()函数调用引导线程销毁。
参数:
thread:终止的同时需要销毁的线程 ID;
返回值:
成功,返回0;失败,返回其它值。
5、多线程并发服务器端实现 简单的聊天程序
【补】 函数 sprint()
由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。
//把整数123 打印成一个字符串保存在s 中。
sprintf(s, “%d”, 123); //产生"123"
// 打印123的字符串,并将 [123] 保存在 name变量中。
sprint(name, “[%s]”, “123”)
6、课后习题
1)单CPU系统中如何同时执行多个进程?请解释该过程中发生的上下文切换
答:因为系统将CPU切分成多个微小的块后分配给多个进程,为了分时使用CPU,需要”上下文切换“过程。”上下文切换“是指,在CPU改变运行对象的过程中农,执行准备的过程将之前执行的进程数据从换出内存,并将待执行额进程数据传到内存的工作区域
2)为何线程的上下文切换速度相对更快?线程间数据交换为何不需要类似IPC的特别技术?
答:因为线程进行上下文切换时不需要切换数据区和堆区。同时,可以利用数据区和堆区进行数据交换
3)请从执行流角度说明进程和线程的区别
进程:在操作系统中构成单独执行流的单位
线程:在进程内构成单独执行流的单位
4)bcd
5)d
6)请说明完全销毁Linux线程的两种方法
答:pthread_join函数和pthread_detach函数。