典型的Linux进程同一时刻只能做一个事情,为了能够同时处理多个事情,引入了线程概念。线程的特点是:
1、 进程的所有线程共享进程的存储空间,线程间可以直接相互访问,这与进程具有独立的运行空间有显著区别。
2、 单进程可以理解为只有一个线程在运行。
3、 多线程的主要困难是线程同步问题,而没有进程的多进程通信困扰。
线程标识
进程使用pid_t数据类型标识,线程使用pthread_t数据类型标识。这是个平台相关的类型,不同实现采用不同的c数据结构对应,因此系统提供了几个可一直的线程id处理函数,如下:
1: #include <pthread.h>
2: int pthread_equal(pthread_t tpid1, pthread_t tpid2);比较两个线程id
3: 返回值:若相等则返回非零值,否则返回零值。
4: pthread_t pthread_self(void);获取线程自身的线程id
5: 返回值:调用者线程自身的线程id。
线程管理
线程创建:
1: #include <pthread.h>
2: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr
3: void *(*start_rtn)(void), void *restrict arg);
4: 返回值:成功返回0,失败返回错误编号。
5: 参数说明:
6: tidp——指向存储创建线程id的地址
7: attr——线程高级控制属性结构,后面具体描述
8: start_rtn——线程创建成功后执行函数,具有一个void*参数
9: arg——线程执行函数参数传递指针
线程终止:
如果进程的任一线程调用了exit、_Exit、_exit函数,那么整个进程都会终止,同时如果结束信号发送给任一线程,整个进程也会终止。
如果要终止单个线程,而不影响其它线程,可以采用的方法是:
1、线程从启动函数中返回,即start_rtn()函数执行完成时线程会终止。
2、线程调用pthread_exit()终止自己。
1: void pthread_exit(void *rval_ptr);
2: 参数rval_ptr是一个void*指针,该指针可以传递给其它线程,使用pthread_join()函数。
3:
4: int pthread_join(pthread_t thread, void **rval);
5: 说明:
6: 1、 调用pthread_join()的线程会阻塞,直到thread标识的线程从调用函数返回或被取消。
7: 2、 等待的线程从调用函数start_rtn()返回时,rval的值为返回码。
8: 3、 等待的线程被取消时,rval的值为PTHREAD_CANCELED。
9: 4、 如果等待的线程的rval_ptr设置为NULL,调用pthread_join()的函数将等待线程退出,不获取线程终止码。
3、线程被同进程中的其它线程取消
1: #include <pthread.h>
2: int pthread_cancel(pthread_t thread);终止参数标识的线程
3: 返回值:成功返回0,失败返回错误码。
4: 说明:调用该函数的线程不会阻塞,它仅提出请求;thread参数标识的线程可以选择忽略终止请求。
线程控制与进程控制对比:
进程 | 线程 | 概述 |
fork | pthread_create | 创建 |
exit | pthread_exit | 退出 |
waitpid | pthread_join | 等待其它终止 |
abort | pthread_cancel | 中止 |
getpid | pthread_self | 获取id |
线程同步
线程没有相互间通信的困扰,各线程可以直接访问对方的数据,主要关注线程同步的措施。
1、互斥量
互斥量声明为pthread_mutex_t类型,静态声明的互斥量可以赋值为PTHREAD_MUTEX_INITALIZER,动态声明的互斥量使用函数进行初始化。
1: #include <pthread.h>
2: int pthread_mutex_init(pthread_mutex_t *restrict mutex, constr mutex_attr_t
3: *attr);
4: 说明:参数attr置NULL,表示以默认属性初始化互斥量。
5: int pthread_mutex_destory(pthread_mutex_t *mutex);
6: 返回值:成功返回0,失败返回错误码。
操作互斥量有三个函数
1: int pthread_mutex_lock(pthread_mutex_t *mutex);
2: int pthread_mutex_trylock(pthread_mutex_t *mutex);
3: int pthread_mutex_unlock(pthread_mutex_t *mutex);
4: 返回值:成功返回0,失败范围错误码。
5: 说明:如果不希望获取互斥量时阻塞,使用pthread_mutex_trylock()函数,当互斥量未锁时,获取互斥量锁,并返回0;否则获取锁失败,函数立即返回EBUSY。
2、读写锁
读写锁有三种状态:读模式加锁状态,写模式加锁状态,不加锁状态。同一时刻,只有一个线程能够进入写模式加锁状态,但允许多个线程进入读模式加锁状态。
读写锁使用pthread_rwlock_t类型变量表示,使用前必须初始化:
1: #include <pthread.h>
2: int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
3: pthread_rwlockattr_t *restrict attr);
4: int pthread_rwlock_destory(pthread_rwlock_t *rwlock);
5: 返回值:成功返回0,失败返回错误码。
读写锁操作函数:
1: #incldue <pthread.h>
2: int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
3: int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
4: int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
5: 返回值:成功返回0,失败返回错误码。
6: 说明:对于pthread_rwlock_rdlock()函数,可能需要检查返回值,多个获取锁可能会失败。
7: 非阻塞版本:
8: int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
9: int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
10: 返回值:成功返回0,失败返回错误码。
11: 说明:调用时若锁处于锁住状态,返回EBUSY。
上面是线程基本操作用到的接口函数,为了能够对线程进行更高级的控制,还有线程控制属性相关内容,本文只关注线程基本内容,感兴趣的读者可以自行查阅其它介绍。