多线程编程
线程在Unix系统下,通常被称为轻量级的进程。一个进行可以有很都进程,每条线程并行执行不同的任务。
多线程的优点
线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。
多线程使用
**多线程在链接时需使用pthread库 **
创建线程 pthread_create
线程创建函数包含四个变量,分别为:
- 一个线程变量名,被创建线程的标识
- 线程的属性指针,缺省为NULL即可
- 被创建线程的程序代码
- 程序代码的参数
For example:
pthread_t thrd1;
pthread_attr_t attr;
void thread_function(void argument);
char *some_argument;
pthread_create(&thrd1, NULL, (void *)&thread_function, (void *) &some_argument);
结束线程 pthread_exit
线程结束调用实例:
pthread_exit(void *retval); //retval用于存放线程结束的退出状态
线程等待 pthread_join
pthread_create调用成功以后,新线程和老线程谁先执行,谁后执行用户是不知道的,这一块取决与操作系统对线程的调度,如果我们需要等待指定线程结束,需要使用pthread_join函数,这个函数实际上类似与多进程编程中的waitpid。 举个例子,以下假设 A 线程调用 pthread_join 试图去操作B线程,该函数将A线程阻塞,直到B线程退出,当B线程退出以后,A线程会收集B线程的返回码。 该函数包含两个参数:
pthread_t th //th是要等待结束的线程的标识
void **thread_return //指针thread_return指向的位置存放的是终止线程的返回状态。
调用实例:
pthread_join(thrd1, NULL);
多线程的同步与互斥
方式一:锁
在主线程中初始化锁为解锁状态
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
在编译时初始化锁为解锁状态
锁初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
访问对象时的加锁操作与解锁操作
pthread_mutex_lock(&mutex) // 加锁
pthread_mutex_unlock(&mutex) // 释放锁
方式二:信号量
锁有一个很明显的缺点,那就是它只有两种状态:锁定与不锁定。
信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实,我们是可以把锁当作一个0-1信号量的。
它们是在/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数
相关函数
在使用semaphore之前,我们需要先引入头文件#include <semaphore.h>
初始化信号量:int sem_init(sem_t *sem, int pshared, unsigned int value);
成功返回0,失败返回-1
参数
sem:指向信号量结构的一个指针
pshared: 不是0的时候,该信号量在进程间共享,否则只能为当前进程的所有线程们共享
value:信号量的初始值
信号量减1操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem);
成功返回0,失败返回-1
参数
sem:指向信号量的一个指针
信号量加1操作 int sem_post(sem_t *sem);
参数与返回同上
销毁信号量 int sem_destroy(sem_t *sem);
参数与返回同上