多线程
1.为什么有了进 程,还要引入线程呢?
使用多线程的 理由之一是 :
和进程相比 ,它是一种非常“节俭”的多任务操作方式。在Linux系统下,启 动一个新的进程必须分配给它独立的地址空间 ,建立众多的数据表来维护它的代码段、堆栈段和数据 段,这是一种" 昂贵 "的多任务 工作方式。
运行于一个进程中的多个线程, 它们之间使用相同的地址空间 ,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间 。据统计,一个进程的开销大约是一个线程开销的30倍左右 。
使用多线程的理由之二是 :
线程间方便的通信机制 。对不同进程来说,它们具有独立的数据空间 ,要进行数据的传递只能通过进程间 通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷 ,而且方便 。
除了以上所说的优点外,多线程程序作为一种多任务、并发的工作方式,有如下优点 :
A.使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU 上。
B. 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分 。这样的程序会利于理解和修改 。
2.创建线程:
int pthread_create(pthread_t * tidp,const pthread_attr_t *attr,void *(*start_rtn)(void),void *arg)
tidp :线程id
attr :线程属性( 通 常为空 )
start_rtn :线程要执行的函 数
arg : start_rtn的参数
3.编译
因为pthread的库不是linux系统的库,所以在进行编译的时候要加上-lpthread
如:
# gcc filename -lpthread
4.终止线程:
如果进程中任何一个线程中调用exit或_exit,那么整个进程都会终止。线程的正常退出方式有:
(1) 线程从启动例程中返回
(2) 线程可以被另一个进程终止
(3) 线程自己调用pthread_exit函数
void pthread_exit(void * rval_ptr)
功 能:终止调用线程
Rval_ptr: 线程退出返回值的指针 。
5.线程等待:
int pthread_join(pthread_t tid,void **rval_ptr)
功 能:阻塞调用线程,直到指定的线程终止 。
Tid : 等待退出的线程id
Rval_ptr :线程退出的返回值的指针
6.线程标识
#include
pthread_t pthread_self(void)
功能 :
获取调用线程的thread identifier
7.清除
线程终止有两种情况:正常终止和非正常终止
正常终止是线程主动调用pthread_exit或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;
非正常终止是线程在其他线程的干预下或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。
不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,如何保证线程终止时能顺利 的释放掉自己所占用的资源,是一个必须考虑解 决的问题 。
8.清除函数
从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止 ,不包括return )都将执行pthread_cleanup_push()所指定的清理函数 。
#include
void pthread_cleanup_push(void (*rtn)(void *),void *arg)
功 能 :
将清除函数压入清除栈
Rtn: 清除函数
Arg: 清除函数的参数
void pthread_cleanup_pop(int execute)
功能 :
将清除函数弹出清除栈
参数 :
Execute执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,非0:执行; 0:不执行
9.线程同步
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过下面这些技术的使用,可以解决线程之间对资源的竞争:
1 互斥量Mutex
2 信号灯Semaphore
3 条件变量Conditions
10.互斥量
为什么需要互斥量 :
Item * p =queue_list;
Queue_list=queue_list->next;
process_job(p); free(p);
当线程1处理完Item *p=queue_list后,系统停止线程1的运行,改而运行线程2。线程2照样取 出头节点,然后进行处理,最后释放了该节点。过了段时间,线程1重新得到运行。而这个时候,p所指向的节点已经被线程2释放掉,而线程1对此毫无知晓。他会接着运行 process_job(p)。而这将导致无法预料的后果!
对于这种情况,系统给我们提供了互斥量 。线程在取出头节点前必须要等待互斥量,如果此时有其他线程已经获得该互斥量,那么该线程将会阻塞在这里。只有等到其他线程释放掉该 互斥量后,该线程才有可能得到该互斥量 。互斥量从本质上说就是一把锁,提供对共享资源 的保护访问 。
11.创建互斥量
在Linux中,互斥量使用类型pthread_mutex_t表示。在使用前,要对它进行初始化 :
A.对于静态分配的互斥量 ,可以把它设置为默认的mutex对 象 PTHREAD_MUTEX_INITIALIZER
B.对于动态分配的互斥量 ,在申请内存(malloc)之后,通过pthread_mutex_init进行初始化 , 并且在释放内存(free)前需要调用pthread_mutex_destroy 。
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)
int pthread_mutex_destroy(pthread_mutex_t *mutex)
12.互斥量PK信号量
Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。
Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人 出来。对于N=1的情况,称为binary semaphore 。Binary semaphore与 Mutex的差异 :
1. mutex要由获得锁的线程来释放( 谁 获 得, 谁 释 放 )。而 semaphore可以由其它线程释放
2.初始状态可能不一样:mutex的初始值是1,而semaphore的初始值可能是0(或者为 1) 。
阅读(2600) | 评论(0) | 转发(0) |