Linux多线程
• 两个函数的第一个参数都是属性。 • pthread_attr_setdetachstate用来设置分离属性,第二个参数 有两个值PTHREAD_CREATE_DETACHED 和
PTHREAD_CREATE_JOINABLE。 • pthread_attr_getdetachstate用来获取分离状态。
说明 线程的分离状态属性 线程栈末尾的警戒缓冲区大小 线程栈的最低地址 线程栈的大小(字节)
线程分离属性
#include <pthread.h> int pthread_detach(pthread_t thread); • 当一个线程被创建时,系统给它创建一个线程控制块(有 thread id来标识)。如果线程没有设置分离属性,那么需 id来标识)。如果线程没有设置分离属性,那么需 要其它线程通过pthread_join来回收这个线程控制块。如果 要其它线程通过pthread_join来回收这个线程控制块。如果 设置了分离属性,那么线程结束时自动释放创建时分配的 资源。 • pthread_detach用来设置分离属性,但须注意必须在新创建 的线程结束之前!一般来说这个函数是有新建线程进入启 动函数后立马调用的,pthread_detach(pthread_self())
ห้องสมุดไป่ตู้
线程池
• 在程序设计中,并不是来一个任务/请求就 在程序设计中,并不是来一个任务/ 创建一个线程,执行完毕线程退出。 • 程序初始化时创建一个线程池,线程池中 有n个线程。 • 当一个请求来时,从线程池中取一个线程, 完成请求后线程再放回线程池。 • 节省了频繁创建线程和销毁线程的开销。
pthread
pthread_join.c
线程ID 线程ID
#include <pthread.h> pthread_t pthread_self(void); int pthread_equal(pthread_t t1, pthread_t t2);
• pthread_self返回调用线程的线程ID。 pthread_self返回调用线程的线程ID。 • 当比较两个线程是否是同一个线程,需要 用pthread_equal.
• pthread_create用来创建一个线程。 pthread_create用来创建一个线程。 • 成功返回0,失败返回非0。 成功返回0,失败返回非0 • thread指向内存单元将被设置为新创建的线 thread指向内存单元将被设置为新创建的线 程ID,attr是要创建线程的属性(NULL为 ID,attr是要创建线程的属性(NULL为 默认属性),start_routine为线程开始执行 默认属性),start_routine为线程开始执行 的函数,arg为start_routine的参数。 的函数,arg为start_routine的参数。 • 线程共享全局变量,在一个线程中改变对 另外线程可见。
线程分离属性
#include <pthread.h> int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
global.c
pthread_create()
• arg不能是局部变量,否则线程使用该变量 arg不能是局部变量,否则线程使用该变量 时可能已经被修改或者不存在了。 • start_routine的参数和返回值都为void *,如果 start_routine的参数和返回值都为void *,如果 有多个参数,那只能打包成一个结构体了。 返回值不能是局部变量!! • 主线程退出,则整个进程结束。
fork()
多线程中的信号
• 在linux系统中信号是用来通知进程特殊事件 linux系统中信号是用来通知进程特殊事件 发生了 • 多线程中有哪个线程来处理呢? • 信号会被递送给恰当的线程,比如SIGILL 信号会被递送给恰当的线程,比如SIGILL 非法指令。 • 信号会被递送给进程中任意的线程处理。 • 在多线程环境我们一般指定一个线程专门 处理信号,其余线程全部屏蔽信号处理。
• guardsize用来设置栈溢出后,还可以使用的 栈的大小,一般为一个PAGESIZE。也称警 戒缓冲区。 • 如果我们对stackaddr做了修改 ,那么系统会 假设我们自己会管理栈,不会提供警戒缓 冲区给我们使用。相当于设置guardsize为0。
线程同步
• 当多个线程共享相同的内存时,就需要确 保每个线程看到一致的数据。 • 如果数据是只读的,那么不存在一致性问 题。 • 如果线程对数据有读有些,这时候就需要 同步机制来保证数据的一致性。
• A POSIX standard (IEEE 1003.1c) API for thread creation and synchronization。 synchronization。 • pthread大概有60多个函数,包括线程的创建、终 pthread大概有60多个函数,包括线程的创建、终 止等。 • Linux中实现了pthread线程库 Linux中实现了pthread线程库 • Unix/Widows中也有pthread的实现。 Unix/Widows中也有pthread的实现。 • Linux中使用pthead方法为: Linux中使用pthead方法为: #include <pthread.h> gcc main.c –lpthread 注意:必须加lpthread,不像glibc库,gcc不会自动去 注意:必须加lpthread,不像glibc库,gcc不会自动去 链接pthread库。 链接pthread库。
• 注意:线程一旦设置了分离状态,再调用 pthread_join就会出错。
pthread_attr_detac h.c
线程栈属性
#include <pthread.h> int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
pthread_exit
#include <pthread.h> void pthread_exit(void *value_ptr); int pthread_join(pthread_t thread, void **value_ptr);
• 调用pthread_exit后,线程终止。 调用pthread_exit后,线程终止。 • 参数 参数value_ptr会被后来调用pthread_join函数 的线程获得! • pthread_join的第一个参数指定某个线程, 第二个参数用来接收线程的退出值。
线程同步
• 需要一种机制确 保变量修改时, 只有一个线程在 访问。这样就能 保证数据的一致 性。
互斥量
• 我们可以使用pthread库提供的互斥量来确 我们可以使用pthread库提供的互斥量来确 保同一时间只有一个线程访问数据。 • 互斥量其实就是一种锁,在访问共享数据 之前设置这个锁,访问完之后释放这个锁。 • 互斥量一旦被加了锁,其他任何线程再也 不能在这个互斥量上加锁,需等到锁被释 放。
create_local. rimary_exit.c
线程终止
• 如果进程中的任意线程调用exit、_exit、 如果进程中的任意线程调用exit、_exit、 _Exit,那么整个进程都会被终止。 _Exit,那么整个进程都会被终止。 • 线程从启动函数中返回,返回值是线程的 退出码。 • 线程可以被其他线程取消 • 线程调用pthread_exit退出。 线程调用pthread_exit退出。
线程属性
• pthread_create的第二个参数就是线程的属性, pthread_create的第二个参数就是线程的属性, 传入NULL表示使用默认属性。 传入NULL表示使用默认属性。 • 可以用过pthread API来修改线程的属性。 可以用过pthread API来修改线程的属性。
属性名称 detachstate guardsize stackaddr stacksize
线程栈属性
#include <pthread.h> int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,size_t *restrict guardsize); int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
main.c
线程创建:pthread_create() 线程创建:
#include <pthread.h> int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);
多线程模型
• 内核线程和用户线程的对应关系 • 一个内核线程对应n个用户线程(用户级线 一个内核线程对应n 程) • 一个内核线程对应一个用户线程(内核级 线程)
用户级线程
内核级线程
多线程中的fork 多线程中的fork
• 在linux系统当一个拥有多线程的进程调用 linux系统当一个拥有多线程的进程调用 fork时,子进程只会有一个线程! fork时,子进程只会有一个线程!