linux多线程全面解析
- fork是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。目前有的Unix实现使用一种叫做写时拷贝(copy-on-write)的技术,可避免父进程数据空间向子进程的拷贝。尽管有这种优化技术,fork仍然是昂贵的。
- 2. fork子进程后,需要用进程间通信(IPC)在父子进程之间传递信息。Fork之前的信息容易传递,因为子进程从一开始就有父进程数据空间及所有描述字的拷贝。但是从子进程返回信息给父进程需要做更多的工作。
1.线程基础介绍:
- 数据结构:
pthread_t:线程的ID
pthread_attr_t:线程的属性
- 操作函数:
pthread_create():创建一个线程
pthread_exit():终止当前线程
pthread_cancel():中断另外一个线程的运行
pthread_join():阻塞当前的线程,直到另外一个线程运行结束
pthread_attr_init():初始化线程的属性
pthread_attr_setdetachstate():设置脱离状态的属性(决定这个线程在终止时是否可以被结合)
pthread_attr_getdetachstate():获取脱离状态的属性
pthread_attr_destroy():删除线程的属性
pthread_kill():向线程发送一个信号
- 同步函数:
用于 mutex 和条件变量
pthread_mutex_init()初始化互斥锁
pthread_mutex_destroy()删除互斥锁
pthread_mutex_lock():占有互斥锁(阻塞操作)
pthread_mutex_trylock():试图占有互斥锁(不阻塞操作)。即,当互斥锁空闲时,将占有该锁;否则,立即返回。
pthread_mutex_unlock():释放互斥锁
pthread_cond_init():初始化条件变量
pthread_cond_destroy():销毁条件变量
pthread_cond_signal():唤醒第一个调用pthread_cond_wait()而进入睡眠的线程
pthread_cond_wait():等待条件变量的特殊条件发生
Thread-local storage(或者以Pthreads术语,称作线程特有数据):
pthread_key_create():分配用于标识进程中线程特定数据的键
pthread_setspecific():为指定线程特定数据键设置线程特定绑定
pthread_getspecific():获取调用线程的键绑定,并将该绑定存储在 value 指向的位置中
pthread_key_delete():销毁现有线程特定数据键
pthread_attr_getschedparam();获取线程优先级
pthread_attr_setschedparam();设置线程优先级
2.概念:
3.线程定义
#include <pthread.h> int pthread_equal(pthread_t tid1,pthread_t tid2)
3)pthread_self函数用于获得本线程的thread id
#include <pthread.h> pthread _t pthread_self(void);
4.线程的创建
#include <pthread.h> int pthread_create(
pthread_t*restrict tidp,
constpthread_attr_t*restrict attr,
void*(*start_rtn)(void*),void*restrict arg);
- pthread_t *restrict tidp:返回最后创建出来的Thread的Thread ID
- const pthread_attr_t *restrict attr:指定线程的Attributes,后面会讲道,现在可以用NULL
- void *(*start_rtn)(void *):指定线程函数指针,该函数返回一个void *,参数也为void*
- void *restrict arg:传入给线程函数的参数
- 返回错误值。
5.线程的退出
- exit, _Exit, _exit用于中止当前进程,而非线程
- 中止线程可以有三种方式:
a. 在线程函数中return
- pthread_exit和pthread_join函数的用法:
#include <pthread.h> void pthread_exit(void*rval_ptr); int pthread_join(pthread_t thread,void**rval_ptr);
- 一个Thread可以要求另外一个Thread被Cancel,通过调用pthread_cancel函数:
#include <pthread.h> void pthread_cancel(pthread_t tid)
该函数会使指定线程如同调用了pthread_exit(PTHREAD_CANCELLED)。不过,指定线程可以选择忽略或者进行自己的处理,在后面会讲到。此外,该函数不会导致Block,只是发送Cancel这个请求。
- 线程可以安排在它退出的时候,某些函数自动被调用,类似atexit()函数。需要调用如下函数:
#include <pthread.h> void pthread_cleanup_push(void(*rtn)(void*),void*arg);
void pthread_cleanup_pop(int execute);
这两个函数维护一个函数指针的Stack,可以把函数指针和函数参数值push/pop。执行的顺序则是从栈顶到栈底,也就是和push的顺序相反。
void*thread_func(void*arg)
{
pthread_cleanup_push(cleanup,“handler”) // do something Pthread_cleanup_pop();
return((void*));
}
- 进程函数和线程函数的相关性:
Process Primitive
|
Thread Primitive
|
Description
|
fork
|
pthread_create
|
创建新的控制流
|
exit
|
pthread_exit
|
退出已有的控制流
|
waitpid
|
pthread_join
|
等待控制流并获得结束代码
|
atexit
|
pthread_cleanup_push
|
注册在控制流退出时候被调用的函数
|
getpid
|
pthread_self
|
获得控制流的id
|
abort
|
pthread_cancel
|
请求非正常退出
|
- 缺省情况下,一个线程A的结束状态被保存下来直到pthread_join为该线程被调用过,也就是说即使线程A已经结束,只要没有线程B调用 pthread_join(A),A的退出状态则一直被保存。而当线程处于Detached状态之时,当线程退出的时候,其资源可以立刻被回收,那么这个退出状态也丢失了。在这个状态下,无法为该线程调用pthread_join函数。我们可以通过调用pthread_detach函数来使指定线程进入 Detach状态:
#include <pthread.h>
int pthread_detach(pthread_t tid);
6.线程同步
- 互斥量:Mutex
各个现成向同一个文件顺序写入数据,最后得到的结果是不可想象的。所以用互斥锁来保证一段时间内只有一个线程在执行一段代码。
#include <pthread.h> int pthread_mutex_init(
pthread_mutex_t*restrict mutex,
constpthread_mutexattr_t*restrict attr) int pthread_mutex_destroy(pthread_mutex_t*mutex);
c. pthread_mutex_lock 用于Lock Mutex,如果Mutex已经被Lock,该函数调用会Block直到Mutex被Unlock,然后该函数会Lock Mutex并返回。pthread_mutex_trylock类似,只是当Mutex被Lock的时候不会Block,而是返回一个错误值EBUSY。
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t*mutex); int pthread_mutex_trylock(pthread_mutex_t*mutex); int pthread_mutex_unlock(pthread_mutex_t*mutex);
void reader_function (void);
void writer_function (void);
char buffer;
int buffer_has_item=;
pthread_mutex_t mutex;
struct timespec delay;
void main (void)
{
pthread_t reader;
/* 定义延迟时间*/
delay.tv_sec =;
delay.tv_nec =;
/* 用默认属性初始化一个互斥锁对象*/
pthread_mutex_init (&mutex,NULL);
pthread_create(&reader, pthread_attr_default,(void*)&reader_function), NULL);
writer_function();
}
void writer_function (void){
while(){
/* 锁定互斥锁*/
pthread_mutex_lock (&mutex);
if(buffer_has_item==){
buffer=make_new_item();
buffer_has_item=;
}
/* 打开互斥锁*/
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
void reader_function(void){
while(){
pthread_mutex_lock(&mutex);
if(buffer_has_item==){
consume_item(buffer);
buffer_has_item=;
}
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
需要注意的是在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,a线程先锁定互斥锁1,b 线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数 pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点
- 读写锁:Reader-Writer Locks
#include <pthread.h> int pthread_rwlock_init(
pthread_rwlock_t*restrict rwlock,
constpthread_rwlockattr_t*restrict attr) int pthread_rwlock_destroy(pthread_rwlock_t*rwlock);
e. 获得读写锁的方法如下:
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t*rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t*rwlock); int pthread_rwlock_unlock(pthread_rwlock_t*rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t*rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t*rwlock);
pthread_rwlock_rdlock:获得读锁
- Conditional Variable:条件变量
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
#include <pthread.h> int pthread_cond_init(
pthread_cond_t*restrict cond,
constpthread_condxattr_t*restrict attr) int pthread_cond_destroy(pthread_cond_t*cond);
#include <pthread.h> int pthread_cond_wait(
pthread_cond_t*restrict cond,
pthread_mutex_t*restrict mutex); int pthread_cond_timedwait(
pthread_cond_t*restrict cond,
pthread_mutex_t*restrict mutex,
conststruct timespec *restrict timeout);
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count (){
pthread_mutex_lock (&count_lock);
while(count==)
pthread_cond_wait(&count_nonzero,&count_lock);
count=count -;
pthread_mutex_unlock (&count_lock);
}
increment_count(){
pthread_mutex_lock(&count_lock);
if(count==)
pthread_cond_signal(&count_nonzero);
count=count+;
pthread_mutex_unlock(&count_lock);
}
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
注意timespec的时间是绝对时间而非相对时间,因此需要先调用gettimeofday函数获得当前时间,再转换成timespec结构,加上偏移量。
#include <pthread.h> int pthread_cond_signal(pthread_cond_t*cond); int pthread_cond_broadcast(pthread_cond_t*cond);
7.线程属性
- 线程属性设置
- 绑定
#include <pthread.h>
pthread_attr_t attr;
pthread_t tid;
/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid,&attr,(void*) my_function, NULL);
- 线程分离状态
线程的分离状态决定一个线程以什么样的方式来终止自己。非分离的线程终止时,其线程ID和退出状态将保留,直到另外一个线程调用 pthread_join.分离的线程在当它终止时,所有的资源将释放,我们不能等待它终止。
- 4.优先级
它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。
#include <pthread.h>
#include <sched.h>
pthread_attr_t attr;pthread_t tid;
sched_param param;
int newprio=;
/*初始化属性*/
pthread_attr_init(&attr);
/*设置优先级*/
pthread_attr_getschedparam(&attr,¶m);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr,¶m);
pthread_create(&tid,&attr,(void*)myfunction, myarg);
8.具体 使用:
linux多线程全面解析的更多相关文章
- Linux多线程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- Linux多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- 《Linux多线程服务器端编程》读书笔记第3章
<Linux多线程服务器端编程>第3章主要讲的是多线程服务器的适用场合与常用的编程模型. 1.进程和线程 一个进程是"内存中正在运行的程序“.每个进程都有自己独立的地址空间(ad ...
- 笔记整理--Linux多线程
Unix高级环境编程系列笔记 (2013/11/17 14:26:38) Unix高级环境编程系列笔记 出处信息 通过这篇文字,您将能够解答如下问题: 如何来标识一个线程? 如何创建一个新线程? 如何 ...
- Linux多线程服务器端编程
目录 Linux多线程服务器端编程 线程安全的对象生命期管理 对象的销毁线程比较难 线程同步精要 借shared_ptr实现写时拷贝(copy-on-write) 多线程服务器的适用场合与常用编程模型 ...
- Linux Pthread 深入解析(转-度娘818)
Linux Pthread 深入解析 Outline - 1.线程特点 - 2.pthread创建 - 3.pthread终止 - 4.mutex互斥量使用框架 - ...
- Linux多线程服务端编程一些总结
能接触这本书是因为上一个项目是用c++开发基于Linux的消息服务器,公司没有使用第三方的网络库,卷起袖子就开撸了.个人因为从业经验较短,主 要负责的是业务方面的编码.本着兴趣自己找了这本书.拿到书就 ...
- 《Linux 多线程服务端编程:使用 muduo C++ 网络库》电子版上市
<Linux 多线程服务端编程:使用 muduo C++ 网络库> 电子版已在京东和亚马逊上市销售. 京东购买地址:http://e.jd.com/30149978.html 亚马逊Kin ...
- [转载]赖勇浩:推荐《Linux 多线程服务器端编程》
推荐<Linux 多线程服务器端编程> 赖勇浩(http://laiyonghao.com) 最近,有一位朋友因为工作需要,需要从网游的客户端编程转向服务器端编程,找我推荐一本书.我推荐了 ...
随机推荐
- LeetCode:N叉树的前序遍历【589】
LeetCode:N叉树的前序遍历[589] 题目描述 给定一个 N 叉树,返回其节点值的前序遍历. 例如,给定一个 3叉树 : 返回其前序遍历: [1,3,5,6,2,4]. 题目分析 使用栈结构. ...
- 【Github教程】史上最全github使用方法:github入门到精通(转自eoeandroid.com)
本文来源:http://www.eoeandroid.com/thread-274556-1-1.html 另附经典教程网址 :http://wuyuans.com/2012/05/github-si ...
- 【Java Web】新手教程(转)
转自:http://www.journaldev.com/1854/java-web-application-tutorial-for-beginners#web-server-client Web ...
- FTP vsftp 安装、管理
FTP简介 FTP是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为文传协议,用户Internet上的控制文件的双向传输. FTP的主要作用,就是让用户链接上一个远 ...
- ES6数组的扩展--Array.from()和Array.of()
一. Array.from() : 将伪数组对象或可遍历对象转换为真数组 1.何为伪数组 如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为"类 ...
- yield、greenlet与协程gevent
yield 在说明yield之前,我们了解python中一些概念. 在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(ge ...
- React Native集成Redux框架讲解与应用
学过React Native的都知道,RN的UI是根据相应组件的state进行render的,而页面又是由大大小小的组件构成,导致每个组件都必须维护自身的一套状态,因此当页面复杂化的时候,管理stat ...
- eclipse向上/下复制一行(或者多行)的快捷键失效的基本解决方法
在eclipse中,快捷键Ctrl+Alt+↓是向下复制选中的行,快捷键Ctrl+Alt+↑是向上复制选中的行. 这两个快捷键也是我常用的快捷键之一,以前也遇到失效. 所以现在记录一个解决的方法: 在 ...
- Android解决软键盘弹出将布局顶到上面
有时候我们在下面的布局是一个RadioGroup,然后当页面中的EditText获得焦点的时候,会将地步的RadioGroup顶起来,这时候我们只需要在AndroidMainfest中RadioGro ...
- Hibernate -- 项目结构模型改造, 加 Utils 和 Dao层
示例代码: App.java 模拟客户端 /** * 模拟客户端 */ public class App { @Test public void saveCustomer(){ CustomerDao ...